From 38a4c42b8960acf13bbe0f3e925b4238e4fcea10 Mon Sep 17 00:00:00 2001 From: Mihai Blaga Date: Mon, 19 Jun 2023 13:39:01 +0300 Subject: [PATCH] updated all plugins --- .../overrides/ios/ImageResizer.mm | 410 ++ .../@lightningjs/cli/overrides.json | 47 +- .../@lightningjs/ui/overrides/src/Keyboard.js | 460 ++ .../src/helpers/CollectionWrapper.js | 593 ++ .../overrides/dist/spatialNavigation.js | 1114 ---- .../overrides/dist/visualDebugger.js | 85 - .../src/DateTimePickerAndroid.android.js | 129 + .../app/overrides/RNFBApp.podspec | 32 + .../overrides@5/src/views/DrawerItemList.tsx | 77 + .../overrides@5/src/views/DrawerView.tsx | 248 + .../react-native/overrides/ios/RNSentry.m | 359 + .../java/renative/helloworld/DetoxTest.java | 24 + .../overrides/android/detox/build.gradle | 140 + .../overrides@9.3.1/dist/build/entries.js | 159 - .../next/overrides@9.3.1/dist/build/index.js | 1248 ---- .../next/overrides@9.3.1/dist/build/utils.js | 748 --- .../dist/build/webpack-config.js | 832 --- .../overrides@9.3.1/dist/cli/next-build.js | 58 - .../next/overrides@9.3.1/dist/cli/next-dev.js | 92 - .../overrides@9.3.1/dist/cli/next-export.js | 79 - .../next/overrides@9.3.1/dist/export/index.js | 492 -- .../dist/lib/find-pages-dir.js | 47 - .../overrides@9.4.3/dist/build/entries.js | 5 - .../next/overrides@9.4.3/dist/build/index.js | 50 - .../overrides@9.4.3/dist/cli/next-build.js | 18 - .../next/overrides@9.4.3/dist/cli/next-dev.js | 25 - .../overrides@9.4.3/dist/cli/next-export.js | 22 - .../next/overrides@9.4.3/dist/export/index.js | 24 - .../dist/lib/find-pages-dir.js | 4 - .../next/overrides@9.4.3/dist/next.patch | 273 - .../{overrides => }/CachedImage.js | 0 .../react-native-cached-image/overrides.json | 7 + .../{overrides => }/utils/fsUtils.js | 0 .../{overrides => }/utils/pathUtils.js | 0 .../overrides@3.40.0/android/build.gradle | 79 + .../overrides@3.40.0/ios/RN/RNCamera.m | 2263 +++++++ .../overrides@4.2.1/android/build.gradle | 81 + .../overrides@4.2.1/ios/RN/RNCamera.m | 2327 +++++++ .../th317erd/react/DynamicFontsModule.java | 160 + .../overrides/ios/RNHomeIndicator.h | 10 + .../overrides/android/build.gradle | 29 + .../overrides/react-native-maps.podspec | 12 +- .../overrides/android/build.gradle | 37 + .../overrides/android/build.gradle | 66 + .../app/src/main/res/drawable/arrow1.png | Bin 0 -> 8410 bytes .../app/src/main/res/drawable/arrow2.png | Bin 0 -> 5740 bytes .../app/src/main/res/drawable/arrow3.png | Bin 0 -> 5759 bytes .../app/src/main/res/drawable/arrow4.png | Bin 0 -> 5769 bytes .../app/src/main/res/drawable/arrow5.png | Bin 0 -> 5768 bytes .../ios/RNVApp/RNPhotoEditor/RNPhotoEditor.h | 8 + .../ios/RNVApp/RNPhotoEditor/RNPhotoEditor.m | 147 + .../builds/ios/Resources/arrow1.png | Bin 0 -> 8410 bytes .../builds/ios/Resources/arrow2.png | Bin 0 -> 5740 bytes .../builds/ios/Resources/arrow3.png | Bin 0 -> 5759 bytes .../builds/ios/Resources/arrow4.png | Bin 0 -> 5769 bytes .../builds/ios/Resources/arrow5.png | Bin 0 -> 5768 bytes .../overrides/android/build.gradle | 46 + .../photoeditor/PhotoEditorActivity.java | 695 ++ .../overrides/src/index.js | 277 +- .../overrides@6.3.3/src/index.js | 515 ++ .../shimo/react/prompt/RNPromptFragment.java | 180 + .../overrides/RNReanimated.podspec | 2 +- .../overrides/RNScreens.podspec | 46 +- .../overrides/android/build.gradle | 27 + .../RSSignatureCaptureMainView.java | 212 + .../RSSignatureCaptureView.java | 382 ++ .../RSSignatureCaptureViewManager.java | 164 + .../overrides/ios/RSSignatureView.m | 250 + .../overrides/ios/RSSignatureViewManager.m | 64 + .../overrides/android/build.gradle | 25 + .../reactlibrary/RNSimpleCompassPackage.java | 23 + .../java/com/reactlibrary/BlurBuilder.java | 47 + .../reactlibrary/RNTShadowViewManager.java | 215 + .../java/com/reactlibrary/ShadowView.java | 158 + .../overrides/index.js | 7 + .../overrides/src/ShadowView.js | 79 + .../overrides/src/ShadowViewTouchable.js | 233 + .../src/ShadowViewTouchable.tv.native.js | 261 + .../android/build.gradle | 54 + .../android/gradle.properties | 2 + .../react-native-svg/overrides/src/xml.tsx | 562 ++ .../ReactAndroid/build.gradle | 601 ++ .../ReactAndroid/gradle.properties | 27 + .../overrides/RNVectorIcons.podspec | 2 +- .../overrides/android/build.gradle | 39 + .../WebViewBridgeManager.java | 78 + .../WebViewBridgePackage.java | 26 + .../overrides/ios/RCTWebViewBridge.m | 474 ++ .../react-native-webview-bridge.podspec | 20 + .../overrides/webview-bridge/index.android.js | 238 + .../overrides/webview-bridge/index.ios.js | 329 + .../overrides/android/build.gradle | 17 +- .../overrides@4.0.2/android/build.gradle | 36 + .../overrides/src/RichTextToolbar.js | 194 + .../Components/Touchable/TVTouchable.js | 55 + .../Components/Touchable/TouchableOpacity.js | 303 + .../Components/Touchable/TouchableOpacity.js | 286 + .../ReactAndroid/build.gradle | 596 ++ .../ReactAndroid/gradle.properties | 27 + .../overrides@0.67.2/scripts/find-node.sh | 62 + .../recyclerlistview/overrides.json | 7 + .../rnv/pluginTemplates/renative.plugins.json | 5798 ++++++++++------- 102 files changed, 18895 insertions(+), 7966 deletions(-) create mode 100644 packages/rnv/pluginTemplates/@bam.tech/react-native-image-resizer/overrides/ios/ImageResizer.mm create mode 100644 packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/Keyboard.js create mode 100644 packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/helpers/CollectionWrapper.js delete mode 100644 packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/spatialNavigation.js delete mode 100644 packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/visualDebugger.js create mode 100644 packages/rnv/pluginTemplates/@react-native-community/datetimepicker/overrides/src/DateTimePickerAndroid.android.js create mode 100644 packages/rnv/pluginTemplates/@react-native-firebase/app/overrides/RNFBApp.podspec create mode 100644 packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerItemList.tsx create mode 100644 packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerView.tsx create mode 100644 packages/rnv/pluginTemplates/@sentry/react-native/overrides/ios/RNSentry.m create mode 100644 packages/rnv/pluginTemplates/detox/builds/android@test/app/src/androidTest/java/renative/helloworld/DetoxTest.java create mode 100644 packages/rnv/pluginTemplates/detox/overrides/android/detox/build.gradle delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/entries.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/index.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/utils.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/webpack-config.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-build.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-dev.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-export.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/export/index.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/lib/find-pages-dir.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/entries.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/index.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-build.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-dev.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-export.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/export/index.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/lib/find-pages-dir.js delete mode 100644 packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/next.patch rename packages/rnv/pluginTemplates/react-native-cached-image/{overrides => }/CachedImage.js (100%) create mode 100644 packages/rnv/pluginTemplates/react-native-cached-image/overrides.json rename packages/rnv/pluginTemplates/react-native-cached-image/{overrides => }/utils/fsUtils.js (100%) rename packages/rnv/pluginTemplates/react-native-cached-image/{overrides => }/utils/pathUtils.js (100%) create mode 100644 packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/ios/RN/RNCamera.m create mode 100644 packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/ios/RN/RNCamera.m create mode 100644 packages/rnv/pluginTemplates/react-native-dynamic-fonts/overrides/android/src/main/java/org/th317erd/react/DynamicFontsModule.java create mode 100644 packages/rnv/pluginTemplates/react-native-home-indicator/overrides/ios/RNHomeIndicator.h create mode 100644 packages/rnv/pluginTemplates/react-native-image-picker/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-pdf-view/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-pdf/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow1.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow2.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow3.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow4.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow5.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.h create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.m create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow1.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow2.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow3.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow4.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow5.png create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/src/main/java/com/ahmedadeltito/photoeditor/PhotoEditorActivity.java create mode 100644 packages/rnv/pluginTemplates/react-native-picker-select/overrides@6.3.3/src/index.js create mode 100644 packages/rnv/pluginTemplates/react-native-prompt-android/overrides/android/src/main/java/im/shimo/react/prompt/RNPromptFragment.java create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureMainView.java create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureView.java create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureViewManager.java create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureView.m create mode 100644 packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureViewManager.m create mode 100644 packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/src/main/java/com/reactlibrary/RNSimpleCompassPackage.java create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/BlurBuilder.java create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/RNTShadowViewManager.java create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/ShadowView.java create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/index.js create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowView.js create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.js create mode 100644 packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.tv.native.js create mode 100644 packages/rnv/pluginTemplates/react-native-splash-screen/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-splash-screen/android/gradle.properties create mode 100644 packages/rnv/pluginTemplates/react-native-svg/overrides/src/xml.tsx create mode 100644 packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/gradle.properties create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/ios/RCTWebViewBridge.m create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/react-native-webview-bridge.podspec create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.android.js create mode 100644 packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.ios.js create mode 100644 packages/rnv/pluginTemplates/react-native-zip-archive/overrides@4.0.2/android/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native-zss-rich-text-editor/overrides/src/RichTextToolbar.js create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TVTouchable.js create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TouchableOpacity.js create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.67.2/Libraries/Components/Touchable/TouchableOpacity.js create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/build.gradle create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/gradle.properties create mode 100644 packages/rnv/pluginTemplates/react-native/overrides@0.67.2/scripts/find-node.sh create mode 100644 packages/rnv/pluginTemplates/recyclerlistview/overrides.json diff --git a/packages/rnv/pluginTemplates/@bam.tech/react-native-image-resizer/overrides/ios/ImageResizer.mm b/packages/rnv/pluginTemplates/@bam.tech/react-native-image-resizer/overrides/ios/ImageResizer.mm new file mode 100644 index 0000000000..48e7f0555e --- /dev/null +++ b/packages/rnv/pluginTemplates/@bam.tech/react-native-image-resizer/overrides/ios/ImageResizer.mm @@ -0,0 +1,410 @@ +#import "ImageResizer.h" +#import +#import +#import + +#if __has_include() +#import +#import +#else +#import "RCTLog.h" +#import "RCTImageLoader.h" +#endif + +NSString *moduleName = @"ImageResizer"; + +@implementation ImageResizer +RCT_EXPORT_MODULE() + +RCT_REMAP_METHOD(createResizedImage, uri:(NSString *)uri width:(double)width height:(double)height format:(NSString *)format quality:(double)quality mode:(NSString *)mode onlyScaleDown:(BOOL)onlyScaleDown rotation:(nonnull NSNumber *)rotation outputPath:(NSString *)outputPath keepMeta:(nonnull NSNumber *)keepMeta resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +{ + [self createResizedImage:uri width:width height:height format:format quality:quality mode:mode onlyScaleDown:onlyScaleDown rotation:rotation outputPath:outputPath keepMeta:keepMeta resolve:resolve reject:reject]; +} + +- (void)createResizedImage:(NSString *)uri width:(double)width height:(double)height format:(NSString *)format quality:(double)quality mode:(NSString *)mode onlyScaleDown:(BOOL)onlyScaleDown rotation:(nonnull NSNumber *)rotation outputPath:(NSString *)outputPath keepMeta:(nonnull NSNumber *)keepMeta resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + @try { + CGSize newSize = CGSizeMake(width, height); + + //Set image extension + NSString *extension = @"jpg"; + if ([format isEqualToString:@"PNG"]) { + extension = @"png"; + } + + NSString* fullPath; + @try { + fullPath = generateFilePath(extension, outputPath); + } @catch (NSException *exception) { + [NSException raise:moduleName format:@"Invalid output path."]; + } + + NSURL *baseURL = [NSURL fileURLWithPath:@"file://"]; + NSURL * fileURL = [[NSURL alloc] initWithString:uri relativeToURL:baseURL]; + + NSError *err; + if ([fileURL checkResourceIsReachableAndReturnError:&err] == NO){ + [NSException raise:moduleName format:@"Image uri invalid."]; + } + + NSData * imageData = [NSData dataWithContentsOfURL:fileURL]; + + UIImage *image; + image = [UIImage imageWithData:imageData]; + NSDictionary * response = transformImage(image, uri, [rotation integerValue], newSize, fullPath, format, (int)quality, [keepMeta boolValue], @{@"mode": mode, @"onlyScaleDown": [NSNumber numberWithBool:onlyScaleDown]}); + resolve(response); + } @catch (NSException *exception) { + RCTLogError([NSString stringWithFormat:@"Code : %@ / Message : %@", exception.name, exception.reason]); + reject(exception.name, exception.reason, nil); + } + }); +} + + + +bool saveImage(NSString * fullPath, UIImage * image, NSString * format, float quality, NSMutableDictionary *metadata) +{ + if(metadata == nil){ + NSData* data = nil; + if ([format isEqualToString:@"JPEG"]) { + data = UIImageJPEGRepresentation(image, quality / 100.0); + } else if ([format isEqualToString:@"PNG"]) { + data = UIImagePNGRepresentation(image); + } + + if (data == nil) { + return NO; + } + + NSFileManager* fileManager = [NSFileManager defaultManager]; + return [fileManager createFileAtPath:fullPath contents:data attributes:nil]; + } + + // process / write metadata together with image data + else{ + + CFStringRef imgType = kUTTypeJPEG; + + if ([format isEqualToString:@"JPEG"]) { + [metadata setObject:@(quality / 100.0) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality]; + } + else if([format isEqualToString:@"PNG"]){ + imgType = kUTTypePNG; + } + else{ + return NO; + } + + NSMutableData * destData = [NSMutableData data]; + + CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destData, imgType, 1, NULL); + + @try{ + CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef) metadata); + + // write final image data with metadata to our destination + if (CGImageDestinationFinalize(destination)){ + + NSFileManager* fileManager = [NSFileManager defaultManager]; + return [fileManager createFileAtPath:fullPath contents:destData attributes:nil]; + } + else{ + return NO; + } + } + @finally{ + @try{ + CFRelease(destination); + } + @catch(NSException *exception){ + NSLog(@"Failed to release CGImageDestinationRef: %@", exception); + } + } + } +} + +NSString * generateFilePath(NSString * ext, NSString * outputPath) +{ + NSString* directory; + + if ([outputPath length] == 0) { + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + directory = [paths firstObject]; + } else { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + if ([outputPath hasPrefix:documentsDirectory]) { + directory = outputPath; + } else { + directory = [documentsDirectory stringByAppendingPathComponent:outputPath]; + } + + NSError *error; + [[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error]; + if (error) { + NSLog(@"Error creating documents subdirectory: %@", error); + @throw [NSException exceptionWithName:@"InvalidPathException" reason:[NSString stringWithFormat:@"Error creating documents subdirectory: %@", error] userInfo:nil]; + } + } + + NSString* name = [[NSUUID UUID] UUIDString]; + NSString* fullName = [NSString stringWithFormat:@"%@.%@", name, ext]; + NSString* fullPath = [directory stringByAppendingPathComponent:fullName]; + + return fullPath; +} + +UIImage * rotateImage(UIImage *inputImage, float rotationDegrees) +{ + + // We want only fixed 0, 90, 180, 270 degree rotations. + const int rotDiv90 = (int)round(rotationDegrees / 90); + const int rotQuadrant = rotDiv90 % 4; + const int rotQuadrantAbs = (rotQuadrant < 0) ? rotQuadrant + 4 : rotQuadrant; + + // Return the input image if no rotation specified. + if (0 == rotQuadrantAbs) { + return inputImage; + } else { + // Rotate the image by 80, 180, 270. + UIImageOrientation orientation = UIImageOrientationUp; + + switch(rotQuadrantAbs) { + case 1: + orientation = UIImageOrientationRight; // 90 deg CW + break; + case 2: + orientation = UIImageOrientationDown; // 180 deg rotation + break; + default: + orientation = UIImageOrientationLeft; // 90 deg CCW + break; + } + + return [[UIImage alloc] initWithCGImage: inputImage.CGImage + scale: 1.0 + orientation: orientation]; + } +} + +float getScaleForProportionalResize(CGSize theSize, CGSize intoSize, bool onlyScaleDown, bool maximize) +{ + float sx = theSize.width; + float sy = theSize.height; + float dx = intoSize.width; + float dy = intoSize.height; + float scale = 1; + + if( sx != 0 && sy != 0 ) + { + dx = dx / sx; + dy = dy / sy; + + // if maximize is true, take LARGER of the scales, else smaller + if (maximize) { + scale = MAX(dx, dy); + } else { + scale = MIN(dx, dy); + } + + if (onlyScaleDown) { + scale = MIN(scale, 1); + } + } + else + { + scale = 0; + } + return scale; +} + + +// returns a resized image keeping aspect ratio and considering +// any :image scale factor. +// The returned image is an unscaled image (scale = 1.0) +// so no additional scaling math needs to be done to get its pixel dimensions +UIImage* scaleImage (UIImage* image, CGSize toSize, NSString* mode, bool onlyScaleDown) +{ + + // Need to do scaling corrections + // based on scale, since UIImage width/height gives us + // a possibly scaled image (dimensions in points) + // Idea taken from RNCamera resize code + CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale); + + // using this instead of ImageHelpers allows us to consider + // rotation variations + CGSize newSize; + + if ([mode isEqualToString:@"stretch"]) { + // Distort aspect ratio + int width = toSize.width; + int height = toSize.height; + + if (onlyScaleDown) { + width = MIN(width, imageSize.width); + height = MIN(height, imageSize.height); + } + + newSize = CGSizeMake(width, height); + } else { + // Either "contain" (default) or "cover": preserve aspect ratio + bool maximize = [mode isEqualToString:@"cover"]; + float scale = getScaleForProportionalResize(imageSize, toSize, onlyScaleDown, maximize); + newSize = CGSizeMake(roundf(imageSize.width * scale), roundf(imageSize.height * scale)); + } + + UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0); + [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} + +// Returns the image's metadata, or nil if failed to retrieve it. +NSMutableDictionary * getImageMeta(NSString * path) +{ + if([path hasPrefix:@"assets-library"]) { + + __block NSMutableDictionary* res = nil; + + ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) + { + + NSDictionary *exif = [[myasset defaultRepresentation] metadata]; + res = [exif mutableCopy]; + + }; + + ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init]; + NSURL *url = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + + [assetslibrary assetForURL:url resultBlock:resultblock failureBlock:^(NSError *error) { NSLog(@"error couldn't image from assets library"); }]; + + return res; + + } else { + + NSData* imageData = nil; + + if ([path hasPrefix:@"data:"] || [path hasPrefix:@"file:"]) { + NSURL *imageUrl = [[NSURL alloc] initWithString:path]; + imageData = [NSData dataWithContentsOfURL:imageUrl]; + + } else { + imageData = [NSData dataWithContentsOfFile:path]; + } + + if(imageData == nil){ + NSLog(@"Could not get image file data to extract metadata."); + return nil; + } + + CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); + + + if(source != nil){ + + CFDictionaryRef metaRef = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL); + + // release CF image + CFRelease(source); + + CFMutableDictionaryRef metaRefMutable = CFDictionaryCreateMutableCopy(NULL, 0, metaRef); + + // release the source meta ref now that we've copie it + CFRelease(metaRef); + + // bridge CF object so it auto releases + NSMutableDictionary* res = (NSMutableDictionary *)CFBridgingRelease(metaRefMutable); + + return res; + + } + else{ + return nil; + } + + } +} + +NSDictionary * transformImage(UIImage *image, + NSString * originalPath, + int rotation, + CGSize newSize, + NSString* fullPath, + NSString* format, + int quality, + BOOL keepMeta, + NSDictionary* options) +{ + if (image == nil) { + [NSException raise:moduleName format:@"Can't retrieve the file from the path."]; + } + + // Rotate image if rotation is specified. + if (0 != (int)rotation) { + image = rotateImage(image, rotation); + if (image == nil) { + [NSException raise:moduleName format:@"Can't rotate the image."]; + } + } + + // Do the resizing + UIImage * scaledImage = scaleImage( + image, + newSize, + options[@"mode"], + [[options objectForKey:@"onlyScaleDown"] boolValue] + ); + + if (scaledImage == nil) { + [NSException raise:moduleName format:@"Can't resize the image."]; + } + + + NSMutableDictionary *metadata = nil; + + // to be consistent with Android, we will only allow JPEG + // to do this. + if(keepMeta && [format isEqualToString:@"JPEG"]){ + + metadata = getImageMeta(originalPath); + + // remove orientation (since we fix it) + // width/height meta is adjusted automatically + // NOTE: This might still leave some stale values due to resize + metadata[(NSString*)kCGImagePropertyOrientation] = @(1); + + } + + // Compress and save the image + if (!saveImage(fullPath, scaledImage, format, quality, metadata)) { + [NSException raise:moduleName format:@"Can't save the image. Check your compression format and your output path"]; + } + + NSURL *fileUrl = [[NSURL alloc] initFileURLWithPath:fullPath]; + NSString *fileName = fileUrl.lastPathComponent; + NSError *attributesError = nil; + NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:&attributesError]; + NSNumber *fileSize = fileAttributes == nil ? 0 : [fileAttributes objectForKey:NSFileSize]; + NSDictionary *response = @{@"path": fullPath, + @"uri": fileUrl.absoluteString, + @"name": fileName, + @"size": fileSize == nil ? @(0) : fileSize, + @"width": @(scaledImage.size.width), + @"height": @(scaledImage.size.height), + }; + + return response; +} + +// Don't compile this code when we build for the old architecture. +#ifdef RCT_NEW_ARCH_ENABLED +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} +#endif +@end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@lightningjs/cli/overrides.json b/packages/rnv/pluginTemplates/@lightningjs/cli/overrides.json index 72380e66d5..78fef0210a 100644 --- a/packages/rnv/pluginTemplates/@lightningjs/cli/overrides.json +++ b/packages/rnv/pluginTemplates/@lightningjs/cli/overrides.json @@ -1,27 +1,26 @@ { "overrides": { - "src/actions/serve.js": { - "'./build',": "process.env.LNG_BUILD_FOLDER," - }, - "src/actions/dist.js": { - "distDir = path.join(baseDistDir, type)": "distDir = path.join(baseDistDir, 'project')", - "if (!fs.existsSync(distDir)) {": "if (true) { // need this to always run, since this dir is created by rnv" - }, - "src/helpers/build.js": { - "path.join(process.cwd(), 'src/index.js'),": "path.join(process.cwd(), process.env.LNG_ENTRY_FILE)," - }, - "src/configs/rollup.es6.config.js": { - "resolve({ mainFields: ['module', 'main', 'browser'] }),": "resolve({ mainFields: ['module', 'main', 'browser'], extensions: process.env.RNV_EXTENSIONS.split(',').map(extension => `.${extension}`) })," - }, - "src/configs/esbuild.es6.config.js": { - "/src/index.js`],": "/${process.env.LNG_ENTRY_FILE}`]," - }, - "src/configs/rollup.es5.config.js": { - "resolve({ mainFields: ['module', 'main', 'browser'] }),": "resolve({ mainFields: ['module', 'main', 'browser'], extensions: process.env.RNV_EXTENSIONS.split(',').map(extension => `.${extension}`) })," - }, - "src/configs/esbuild.es5.config.js": { - "/src/index.js`],": "/${process.env.LNG_ENTRY_FILE}`]," - } + "src/actions/serve.js": { + "'./build',": "process.env.LNG_BUILD_FOLDER," + }, + "src/actions/dist.js": { + "distDir = path.join(baseDistDir, type)": "distDir = path.join(baseDistDir, 'project')", + "if (!fs.existsSync(distDir)) {": "if (true) { // need this to always run, since this dir is created by rnv" + }, + "src/helpers/build.js": { + "path.join(process.cwd(), enterFile),": "path.join(process.cwd(), process.env.LNG_ENTRY_FILE)," + }, + "src/configs/rollup.es6.config.js": { + "resolve({ extensions, mainFields: buildHelpers.getResolveConfigForBundlers() }),": "resolve({ extensions: process.env.RNV_EXTENSIONS.split(',').map(extension => `.${extension}`), mainFields: buildHelpers.getResolveConfigForBundlers() })," + }, + "src/configs/esbuild.es6.config.js": { + "/src/index.js`],": "/${process.env.LNG_ENTRY_FILE}`]," + }, + "src/configs/rollup.es5.config.js": { + "resolve({ extensions, mainFields: buildHelpers.getResolveConfigForBundlers() }),": "resolve({ extensions: process.env.RNV_EXTENSIONS.split(',').map(extension => `.${extension}`), mainFields: buildHelpers.getResolveConfigForBundlers() })," + }, + "src/configs/esbuild.es5.config.js": { + "/src/index.js`],": "/${process.env.LNG_ENTRY_FILE}`]," + } } -} - +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/Keyboard.js b/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/Keyboard.js new file mode 100644 index 0000000000..4dac62da60 --- /dev/null +++ b/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/Keyboard.js @@ -0,0 +1,460 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2021 Metrological + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Lightning from '@lightningjs/core'; + +import KeyWrapper from './helpers/KeyWrapper.js'; +import Key from './Key.js'; + +export default class Keyboard extends Lightning.Component { + static _template() { + return { + collision: true, + Keys: { + collision: true, + w: w => w + } + } + } + + _construct() { + this._input = ''; + this._inputField = undefined; + this._maxCharacters = 56; + this.navigationWrapAround = false; + this.resetFocus(); + } + + resetFocus() { + this._columnIndex = 0; + this._rowIndex = 0; + this._previousKey = null; + } + + _setup() { + this._keys = this.tag('Keys'); + this._update(); + } + + _update() { + const {layouts, buttonTypes = {}, styling = {}} = this._config; + if(!this._layout || (this._layout && layouts[this._layout] === undefined)) { + console.error(`Configured layout "${this._layout}" does not exist. Picking first available: "${Object.keys(layouts)[0]}"`); + this._layout = Object.keys(layouts)[0]; + } + const {horizontalSpacing = 0, verticalSpacing = 0, align = 'left'} = styling; + let rowPosition = 0; + const isEvent = /^[A-Z][A-Za-z0-9]{1}/; + const hasLabel = /\:/; + + if(buttonTypes.default === undefined) { + buttonTypes.default = Key; + } + + this._keys.children = layouts[this._layout].map((row, rowIndex) => { + const { + x = 0, + margin = 0, + marginRight, + marginLeft, + marginTop, + marginBottom, + spacing: rowHorizontalSpacing = horizontalSpacing || 0, + align: rowAlign = align + } = styling[`Row${rowIndex+1}`] || {}; + + let keyPosition = 0; + let rowHeight = 0; + const rowKeys = row.map((key, keyIndex) => { + const origin = key; + let keyType = buttonTypes.default; + let action = 'Input'; + let label = key; + + if(isEvent.test(key)) { + if(hasLabel.test(key)) { + key = key.split(':'); + label = key[1].toString(); + key = key[0]; + } + if(buttonTypes[key]) { + keyType = buttonTypes[key]; + action = key.action || key; + } + } + + const keySpacing = keyType.margin || keyType.type.margin; + const { + w = keyType.type.width || 0, + h = keyType.type.height || 0, + marginLeft = keyType.type.marginLeft || keySpacing || 0, + marginRight = keyType.type.marginRight || keySpacing || rowHorizontalSpacing, + } = keyType; + + rowHeight = h > rowHeight ? h : rowHeight; + const currentPosition = keyPosition + marginLeft; + keyPosition += marginLeft + w + marginRight; + return {ref: `Key-{${keyIndex + 1}}`, type: KeyWrapper, keyboard: this, x: currentPosition, w, h, key: {data: {origin, key, label, action}, w, h, ...keyType}} + }); + + let rowOffset = x + (marginLeft || margin); + let rowMount = 0; + + if(this.w && rowAlign === 'center') { + rowOffset = this.w / 2; + rowMount = 0.5; + } + + if(this.w && rowAlign === 'right') { + rowOffset = this.w - (marginRight || margin); + rowMount = 1; + } + + const currentPosition = rowPosition + (marginTop|| margin); + rowPosition = currentPosition + rowHeight + (marginBottom || margin || verticalSpacing); + return { + ref: `Row-${rowIndex + 1}`, + x: rowOffset, + mountX: rowMount, + w: keyPosition, + y: currentPosition, + children: rowKeys + } + }); + this._refocus(); + } + + _getFocused() { + return this.currentKeyWrapper || this; + } + + _handleHover() { + return this.currentKeyWrapper || this; + } + + _handleRight() { + return this.navigate('row', 1); + } + + _handleLeft() { + return this.navigate('row', -1); + } + + _handleUp() { + return this.navigate('column', -1); + } + + _handleDown() { + return this.navigate('column', 1); + } + + _handleKey({key, code = 'CustomKey'}) { + if(code === 'Backspace' && this._input.length === 0) { + return false; + } + if(key === ' ') { + key = 'Space'; + } + const targetFound = this._findKey(key); + if(targetFound) { + this._handleEnter(); + } + return targetFound; + } + + _findKey(str) { + const rows = this._config.layouts[this._layout]; + let i = 0, j = 0; + for(; i < rows.length; i++) { + for (j = 0; j < rows[i].length; j++) { + let key = rows[i][j]; + if((str.length > 1 && key.indexOf(str) > -1) || key.toUpperCase() === str.toUpperCase()) { + this._rowIndex = i; + this._columnIndex = j; + return true; + } + } + } + return false; + } + + $onKeyWrapperClick(currentKey) { + const {origin, action} = currentKey; + const event = { + index: this._input.length, + key: origin + }; + if(this._inputField && this._inputField.cursorIndex) { + event.index = this._inputField.cursorIndex; + } + if(action !== 'Input') { + const split = event.key.split(':') + const call = `on${split[0]}`; + const eventFunction = this[call]; + event.key = split[1]; + if(eventFunction && eventFunction.apply && eventFunction.call) { + eventFunction.call(this, event); + } + this.signal(call, {input: this._input, keyboard: this, ...event}); + } + else { + this.addAt(event.key, event.index); + } + } + + _onPressAction() { + const {origin, action} = this.currentKey.data; + const event = { + index: this._input.length, + key: origin + }; + if(this._inputField && this._inputField.cursorIndex) { + event.index = this._inputField.cursorIndex; + } + if(action !== 'Input') { + const split = event.key.split(':') + const call = `on${split[0]}`; + const eventFunction = this[call]; + event.key = split[1]; + if(eventFunction && eventFunction.apply && eventFunction.call) { + eventFunction.call(this, event); + } + this.signal(call, {input: this._input, keyboard: this, ...event}); + } + else { + this.addAt(event.key, event.index); + } + } + + _handleEnter() { + this._onPressAction(); + } + + _handleClick() { + this._onPressAction(); + } + + + _changeInput(input) { + if(input.length > this._maxCharacters) { + return; + } + const eventData = { + previousInput: this._input, + input: this._input = input + }; + if(this._inputField && this._inputField.onInputChanged) { + this._inputField.onInputChanged(eventData); + } + this.signal('onInputChanged', eventData); + } + + focus(str) { + this._findKey(str); + } + + add(str) { + this._changeInput(this._input + str); + } + + addAt(str, index) { + if(index > this._input.length - 1) { + this.add(str); + } + else if(index > -1) { + this._changeInput(this._input.substring(0, index) + str + this._input.substring(index, this._input.length)); + } + } + + remove() { + this._changeInput(this._input.substring(0, this._input.length - 1)); + } + + removeAt(index) { + if(index > this._input.length - 1) { + this.remove(); + } + else if(index > -1) { + this._changeInput(this._input.substring(0, index-1) + this._input.substring(index, this._input.length)); + } + } + + clear() { + this._changeInput(''); + } + + layout(key) { + if(key === this._layout) { + return; + } + this._layout = key; + if(this.attached) { + this.resetFocus(); + this._update(); + } + } + + inputField(component) { + if(component && component.isComponent) { + this._rowIndex = 0; + this._columnIndex = 0; + this._input = component.input !== undefined? component.input : ''; + this._inputField = component; + } + else { + this._rowIndex = 0; + this._columnIndex = 0; + this._input = '' + this._inputField = undefined; + } + } + + navigate(direction, shift) { + const targetIndex = (direction === 'row' ? this._columnIndex : this._rowIndex) + shift; + const currentRow = this.rows[this._rowIndex]; + if(direction === 'row' && targetIndex > -1 && targetIndex < currentRow.children.length) { + this._previous = null; + return this._columnIndex = targetIndex; + } else if (direction === 'row' && this.navigationWrapAround) { + this._previous = null; + let rowLen = currentRow.children.length + return this._columnIndex = (targetIndex%rowLen + rowLen)%rowLen + } + if(direction === 'column' && targetIndex > -1 && targetIndex < this.rows.length ) { + const currentRowIndex = this._rowIndex; + const currentColumnIndex = this._columnIndex; + if(this._previous && this._previous.row === targetIndex) { + const tmp = this._previous.column; + this._previous.column = this._columnIndex; + this._columnIndex = tmp; + this._rowIndex = this._previous.row; + } + else { + const targetRow = this.rows[targetIndex]; + const currentKey = this.currentKeyWrapper; + const currentRow = this.rows[this._rowIndex]; + const currentX = currentRow.x - (currentRow.w * currentRow.mountX) + currentKey.x; + const m = targetRow.children.map((key) => { + const keyX = targetRow.x - (targetRow.w * targetRow.mountX) + key.x; + if(keyX <= currentX && currentX < keyX + key.w) { + return (keyX + key.w) - currentX; + } + if(keyX >= currentX && keyX <= currentX + currentKey.w) { + return (currentX + currentKey.w) - keyX; + } + return -1; + }); + let acc = -1; + let t = -1; + + for(let i = 0; i < m.length; i++) { + if(m[i] === -1 && acc > -1) { + break; + } + if(m[i] > acc) { + acc = m[i]; + t = i; + } + } + if(t > -1) { + this._rowIndex = targetIndex; + this._columnIndex = t; + } // if no next row found and wraparound is on, loop back to first row + else if(this.navigationWrapAround){ + this._columnIndex = Math.min(this.rows[0].children.length-1, this._columnIndex) + return this._rowIndex = 0; + } + } + if(this._rowIndex !== currentRowIndex) { + this._previous = {column: currentColumnIndex, row: currentRowIndex}; + return this._rowIndex = targetIndex; + } + } + else if(direction === 'column' && this.navigationWrapAround){ + this._previous = {column: this._columnIndex, row: this._rowIndex}; + let nrRows = this.rows.length + this._rowIndex = (targetIndex%nrRows + nrRows)%nrRows + this._columnIndex = Math.min(this.rows[this._rowIndex].children.length-1, this._columnIndex) + } + return false; + } + + onSpace({index}) { + this.addAt(' ', index); + } + + onBackspace({index}) { + this.removeAt(index); + } + + onClear() { + this.clear(); + } + + onLayout({key}) { + this.layout(key); + } + + set config(obj) { + this._config = obj; + if(this.active) { + this._update(); + } + } + + get config() { + return this._config; + } + + set currentInputField(component) { + this.inputField(component); + } + + get currentInputField() { + return this._inputField; + } + + set currentLayout(str) { + this.layout(str); + } + + get currentLayout() { + return this._layout; + } + + set maxCharacters(num) { + this._maxCharacters = num; + } + + get maxCharacters() { + return this._maxCharacters; + } + + get rows() { + return this._keys && this._keys.children; + } + + get currentKeyWrapper() { + return this.rows && this.rows[this._rowIndex].children[this._columnIndex]; + } + + get currentKey() { + return this.currentKeyWrapper && this.currentKeyWrapper.key + } +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/helpers/CollectionWrapper.js b/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/helpers/CollectionWrapper.js new file mode 100644 index 0000000000..1f8d7f648c --- /dev/null +++ b/packages/rnv/pluginTemplates/@lightningjs/ui/overrides/src/helpers/CollectionWrapper.js @@ -0,0 +1,593 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2021 Metrological + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Lightning from '@lightningjs/core'; +import { limitWithinRange } from './index.js'; + +export default class CollectionWrapper extends Lightning.Component { + static _template() { + return { + collision: true, + Wrapper: { + collision: true + } + } + } + + _construct() { + this._direction = CollectionWrapper.DIRECTION.row; + this._scrollTransitionSettings = this.stage.transitions.createSettings({}); + + this._spacing = 0; + this._autoResize = false; + + this._requestingItems = false; + this._requestThreshold = 1; + this._requestsEnabled = false; + + this._gcThreshold = 5; + this._gcIncrement = 0; + this._forceLoad = false; + this.clear(); + } + + _setup() { + this._updateScrollTransition(); + } + + _updateScrollTransition() { + const axis = this._direction === 1 ? 'y' : 'x'; + this.wrapper.transition(axis, this._scrollTransitionSettings); + this._scrollTransition = this.wrapper.transition(axis); + } + + _indexChanged(obj) { + let {previousIndex:previous, index:target, dataLength:max, mainIndex, previousMainIndex, lines} = obj; + if(!isNaN(previousMainIndex) && !isNaN(mainIndex) && !isNaN(lines)) { + previous = previousMainIndex; + target = mainIndex; + max = lines; + } + if(this._requestsEnabled && !this._requestingItems) { + if(previous < target && target + this._requestThreshold >= max) { + this._requestingItems = true; + this.signal('onRequestItems', obj) + .then((response) => { + const type = typeof response; + if(Array.isArray(response) || type === 'object' || type === 'string' || type === 'number') { + this.add(response); + } + if(response === false) { + this.enableRequests = false; + } + this._requestingItems = false; + }) + } + } + + this._refocus(); + this.scrollCollectionWrapper(obj); + this.signal('onIndexChanged', obj); + } + + setIndex(index) { + const targetIndex = limitWithinRange(index, 0, this._items.length - 1); + const previousIndex = this._index; + this._index = targetIndex; + this._indexChanged({previousIndex, index: targetIndex, dataLength: this._items.length}); + return previousIndex !== targetIndex; + } + + clear() { + this._uids = []; + this._items = []; + this._index = 0; + if (this._scrollTransition) { + this._scrollTransition.reset(0, 1); + } + if(this.wrapper) { + const hadChildren = this.wrapper.children > 0; + this.wrapper.patch({ + x: 0, y: 0, children: [] + }); + if(hadChildren) { + this._collectGarbage(true); + } + } + } + + add(item) { + this.addAt(item); + } + + addAt(item, index = this._items.length) { + if(index >= 0 && index <= this._items.length) { + if(!Array.isArray(item)) { + item = [item]; + } + const items = this._normalizeDataItems(item); + this._items.splice(index, 0, ...items); + this.plotItems(); + this.setIndex(this._index); + } + else { + throw new Error('addAt: The index ' + index + ' is out of bounds ' + this._items.length); + } + } + + remove(item) { + if(this.hasItems && item.assignedID) { + for(let i = 0; i < this.wrapper.children.length; i++) { + if(this.wrapper.children[i].assignedID === item.assignedID) { + return this.removeAt(i); + } + } + } + else { + throw new Error('remove: item not found'); + } + } + + removeAt(index, amount = 1) { + if(index < 0 && index >= this._items.length) { + throw new Error('removeAt: The index ' + index + ' is out of bounds ' + this._items.length); + } + const item = this._items[index]; + this._items.splice(index, amount); + this.plotItems(); + return item; + } + + reload(item) { + this.clear(); + this.add(item) + } + + plotItems(items, options) { + //placeholder + } + + reposition(time = 70) { + if(this._repositionDebounce) { + clearTimeout(this._repositionDebounce); + } + this._repositionDebounce = setTimeout(() => { + this.repositionItems(); + }, time); + } + + repositionItems() { + //placeHolder + this.signal('onItemsRepositioned') + } + + up() { + return this._attemptNavigation(-1, 1); + } + + down() { + return this._attemptNavigation(1, 1); + } + + left() { + return this._attemptNavigation(-1, 0); + } + + right() { + return this._attemptNavigation(1, 0); + } + + first() { + return this.setIndex(0); + } + + last() { + return this.setIndex(this._items.length - 1); + } + + next() { + return this.setIndex(this._index + 1); + } + + previous() { + return this.setIndex(this._index - 1); + } + + _attemptNavigation(shift, direction) { + if(this.hasItems) { + return this.navigate(shift, direction); + } + return false; + } + + navigate(shift, direction = this._direction) { + if(direction !== this._direction) { + return false; + } + return this.setIndex(this._index + shift); + } + + scrollCollectionWrapper(obj) { + let {previousIndex:previous, index:target, dataLength:max, mainIndex, previousMainIndex, lines} = obj; + if(!isNaN(previousMainIndex) && !isNaN(mainIndex) && !isNaN(lines)) { + previous = previousMainIndex; + target = mainIndex; + max = lines; + } + const {directionIsRow, main, mainDim, mainMarginFrom, mainMarginTo} = this._getPlotProperties(this._direction); + const cw = this.currentItemWrapper; + let bound = this[mainDim]; + if(bound === 0) { + bound = directionIsRow ? 1920 : 1080; + } + const offset = Math.min(this.wrapper[main], this._scrollTransition && this._scrollTransition.targetValue || 0); + + const sizes = this._getItemSizes(cw); + const marginFrom = (sizes[mainMarginFrom] || sizes.margin || 0); + const marginTo = (sizes[mainMarginTo] || sizes.margin || 0); + + let scroll = this._scroll; + + if(!isNaN(scroll)) { + if(scroll >= 0 && scroll <= 1) { + scroll = bound * scroll - (cw[main] + cw[mainDim] * scroll); + } + else { + scroll = scroll - cw[main]; + } + } + else if(typeof scroll === 'function') { + scroll = scroll.apply(this, [cw, obj]); + } + else if(typeof scroll === 'object') { + const {jump = false, after = false, backward = 0.0, forward = 1.0} = scroll; + if(jump) { + let mod = target % jump; + if(mod === 0) { + scroll = marginFrom - cw[main]; + } + if(mod === jump - 1) { + const actualSize = marginFrom + cw[mainDim] + marginTo; + scroll = (mod * actualSize) + marginFrom - cw[main]; + } + } + else if(after) { + scroll = 0; + if(target >= after - 1) { + const actualSize = marginFrom + cw[mainDim] + marginTo; + scroll = ((after - 1) * actualSize) + marginFrom - cw[main]; + } + } + else { + const backwardBound = bound * this._normalizePixelToPercentage(backward, bound); + const forwardBound = bound * this._normalizePixelToPercentage(forward, bound); + if(target < max - 1 && (previous < target && offset + cw[main] + cw[mainDim] > forwardBound)) { + scroll = forwardBound - (cw[main] + cw[mainDim]); + } + else if(target > 0 && (target < previous && offset + cw[main] < backwardBound)) { + scroll = backwardBound - cw[main]; + } + else if(target === max - 1) { + scroll = bound - (cw[main] + cw[mainDim]); + } + else if(target === 0) { + scroll = marginFrom - cw[main]; + } + } + } + else if(isNaN(scroll)){ + if(previous < target && offset + cw[main] + cw[mainDim] > bound) { + scroll = bound - (cw[main] + cw[mainDim]) + } + else if(target < previous && offset + cw[main] < 0) { + scroll = marginFrom - cw[main] + } + } + + if(this.active && !isNaN(scroll) && this._scrollTransition) { + if(this._scrollTransition.isRunning()) { + this._scrollTransition.reset(scroll, 0.05); + } + else { + this._scrollTransition.start(scroll); + } + } + else if(!isNaN(scroll)) { + this.wrapper[main] = scroll + } + } + + $childInactive({child}) { + if(typeof child === 'object') { + const index = child.componentIndex; + for(let key in this._items[index]) { + if(child.component[key] !== undefined) { + this._items[index][key] = child.component[key]; + } + } + } + this._collectGarbage(); + } + + $getChildComponent({index}) { + return this._items[index]; + } + + _resizeWrapper(crossSize) { + let obj = crossSize; + if(!isNaN(crossSize)) { + const {main, mainDim, crossDim} = this._getPlotProperties(this._direction); + const lastItem = this.wrapper.childList.last; + obj = { + [mainDim]: lastItem[main] + lastItem[mainDim], + [crossDim]: crossSize + } + } + this.wrapper.patch(obj); + if(this._autoResize) { + this.patch(obj); + } + } + + _generateUniqueID() { + let id = ''; + while(this._uids[id] || id === '') { + id = Math.random().toString(36).substr(2, 9); + } + this._uids[id] = true; + return id; + } + + _getPlotProperties(direction) { + const directionIsRow = direction === 0; + return { + directionIsRow: directionIsRow ? true : false, + mainDirection: directionIsRow ? 'rows' : 'columns', + main: directionIsRow ? 'x' : 'y', + mainDim: directionIsRow ? 'w' : 'h', + mainMarginTo: directionIsRow ? 'marginRight' : 'marginBottom', + mainMarginFrom: directionIsRow ? 'marginLeft' : 'marginUp', + crossDirection: !directionIsRow ? 'columns' : 'rows', + cross: directionIsRow ? 'y' : 'x', + crossDim: directionIsRow ? 'h' : 'w', + crossMarginTo: directionIsRow ? 'marginBottom' : 'marginRight', + crossMarginFrom: directionIsRow ? 'marginUp' : 'marginLeft', + } + } + + _getItemSizes(item) { + const itemType = item.type; + if(item.component && item.component.__attached) { + item = item.component; + } + return { + w: item.w || (itemType && itemType['width']), + h: item.h || (itemType && itemType['height']), + margin: item.margin || (itemType && itemType['margin']) || 0, + marginLeft: item.marginLeft || (itemType && itemType['marginLeft']), + marginRight: item.marginRight || (itemType && itemType['marginRight']), + marginTop: item.marginTop || (itemType && itemType['marginTop']), + marginBottom: item.marginBottom || (itemType && itemType['marginBottom']) + } + } + + _collectGarbage(immediate) { + this._gcIncrement++; + if(immediate || (this.active && this._gcThreshold !== 0 && this._gcIncrement >= this._gcThreshold)) { + this._gcIncrement = 0; + this.stage.gc(); + } + } + + _normalizeDataItems(array) { + return array.map((item, index) => { + return this._normalizeDataItem(item) || index; + }) + .filter((item) => { + if(!isNaN(item)) { + console.warn(`Item at index: ${item}, is not a valid item. Removing it from dataset`); + return false; + } + return true; + }); + } + + _normalizeDataItem(item, index) { + if(typeof item === 'string' || typeof item === 'number') { + item = {label: item.toString()} + } + if(typeof item === 'object') { + let id = this._generateUniqueID(); + return {assignedID: id, type: this.itemType, collectionWrapper: this, isAlive: false, ...item} + } + return index; + } + + _normalizePixelToPercentage(value, max) { + if(value && value > 1) { + return value / max; + } + return value || 0; + } + + _getFocused() { + if(this.hasItems) { + return this.currentItemWrapper; + } + return this; + } + + _handleRight() { + return this.right() + } + + _handleLeft() { + return this.left() + } + + _handleUp() { + return this.up(); + } + + _handleDown() { + return this.down(); + } + + _inactive() { + if(this._repositionDebounce) { + clearTimeout(this._repositionDebounce); + } + this._collectGarbage(true); + } + + static get itemType() { + return undefined; + } + + set forceLoad(bool) { + this._forceLoad = bool; + } + + get forceLoad() { + return this._forceLoad; + } + + get requestingItems() { + return this._requestingItems; + } + + set requestThreshold(num) { + this._requestThreshold = num; + } + + get requestThreshold() { + return this._requestThreshold; + } + + set enableRequests(bool) { + this._requestsEnabled = bool + } + + get enableRequests() { + return this._requestsEnabled + } + + set gcThreshold(num) { + this._gcThreshold = num; + } + + get gcThreshold() { + return this._gcThreshold; + } + + get wrapper() { + return this.tag('Wrapper'); + } + + get hasItems() { + return this.wrapper && this.wrapper.children && this.wrapper.children.length > 0; + } + + get currentItemWrapper() { + return this.wrapper.children[this._index]; + } + + get currentItem() { + return this.currentItemWrapper.component; + } + + set direction(string) { + this._direction = CollectionWrapper.DIRECTION[string] || CollectionWrapper.DIRECTION.row; + } + + get direction() { + return Object.keys(CollectionWrapper.DIRECTION)[this._direction]; + } + + set items(array) { + this.clear(); + this.add(array); + } + + get items() { + const itemWrappers = this.itemWrappers; + return this._items.map((item, index) => { + if(itemWrappers[index] && itemWrappers[index].component.isAlive) { + return itemWrappers[index].component; + } + return item; + }); + } + + get length() { + return this._items.length; + } + + set index(index) { + this.setIndex(index); + } + + get itemWrappers() { + return this.wrapper.children; + } + + get index() { + return this._index; + } + + set scrollTransition(obj) { + this._scrollTransitionSettings.patch(obj); + if(this.active) { + this._updateScrollTransition(); + } + } + + get scrollTransition() { + return this._scrollTransition; + } + + set scroll(value) { + this._scroll = value; + } + + get scrollTo() { + return this._scroll; + } + + set autoResize(bool) { + this._autoResize = bool; + } + + get autoResize() { + return this._autoResize; + } + + set spacing(num) { + this._spacing = num; + } + + get spacing() { + return this._spacing; + } +} + +CollectionWrapper.DIRECTION = { + row: 0, + column: 1 +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/spatialNavigation.js b/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/spatialNavigation.js deleted file mode 100644 index fcc74bfb84..0000000000 --- a/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/spatialNavigation.js +++ /dev/null @@ -1,1114 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getChildClosestToOrigin = exports.ROOT_FOCUS_KEY = undefined; - -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _DEFAULT_KEY_MAP; - -var _filter = require('lodash/filter'); - -var _filter2 = _interopRequireDefault(_filter); - -var _first = require('lodash/first'); - -var _first2 = _interopRequireDefault(_first); - -var _sortBy = require('lodash/sortBy'); - -var _sortBy2 = _interopRequireDefault(_sortBy); - -var _findKey = require('lodash/findKey'); - -var _findKey2 = _interopRequireDefault(_findKey); - -var _forEach = require('lodash/forEach'); - -var _forEach2 = _interopRequireDefault(_forEach); - -var _forOwn = require('lodash/forOwn'); - -var _forOwn2 = _interopRequireDefault(_forOwn); - -var _throttle = require('lodash/throttle'); - -var _throttle2 = _interopRequireDefault(_throttle); - -var _difference = require('lodash/difference'); - -var _difference2 = _interopRequireDefault(_difference); - -var _measureLayout = require('./measureLayout'); - -var _measureLayout2 = _interopRequireDefault(_measureLayout); - -var _visualDebugger = require('./visualDebugger'); - -var _visualDebugger2 = _interopRequireDefault(_visualDebugger); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var ROOT_FOCUS_KEY = exports.ROOT_FOCUS_KEY = 'SN:ROOT'; - -var ADJACENT_SLICE_THRESHOLD = 0.2; - -/** - * Adjacent slice is 5 times more important than diagonal - */ -var ADJACENT_SLICE_WEIGHT = 5; -var DIAGONAL_SLICE_WEIGHT = 1; - -/** - * Main coordinate distance is 5 times more important - */ -var MAIN_COORDINATE_WEIGHT = 5; - -var DIRECTION_LEFT = 'left'; -var DIRECTION_RIGHT = 'right'; -var DIRECTION_UP = 'up'; -var DIRECTION_DOWN = 'down'; -var KEY_ENTER = 'enter'; - -var DEFAULT_KEY_MAP = (_DEFAULT_KEY_MAP = {}, _defineProperty(_DEFAULT_KEY_MAP, DIRECTION_LEFT, 37), _defineProperty(_DEFAULT_KEY_MAP, DIRECTION_UP, 38), _defineProperty(_DEFAULT_KEY_MAP, DIRECTION_RIGHT, 39), _defineProperty(_DEFAULT_KEY_MAP, DIRECTION_DOWN, 40), _defineProperty(_DEFAULT_KEY_MAP, KEY_ENTER, 13), _DEFAULT_KEY_MAP); - -var DEBUG_FN_COLORS = ['#0FF', '#FF0', '#F0F']; - -var THROTTLE_OPTIONS = { - leading: true, - trailing: false -}; - -var getChildClosestToOrigin = exports.getChildClosestToOrigin = function getChildClosestToOrigin(children) { - var childrenClosestToOrigin = (0, _sortBy2.default)(children, function (_ref) { - var layout = _ref.layout; - return Math.abs(layout.left) + Math.abs(layout.top); - }); - - return (0, _first2.default)(childrenClosestToOrigin); -}; - -/* eslint-disable no-nested-ternary */ - -var SpatialNavigation = function () { - _createClass(SpatialNavigation, [{ - key: 'sortSiblingsByPriority', - - - /** - * Inspired by: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS_for_TV/TV_remote_control_navigation#Algorithm_design - * Ref Corners are the 2 corners of the current component in the direction of navigation - * They used as a base to measure adjacent slices - */ - value: function sortSiblingsByPriority(siblings, currentLayout, direction, focusKey) { - var _this = this; - - var isVerticalDirection = direction === DIRECTION_DOWN || direction === DIRECTION_UP; - - var refCorners = SpatialNavigation.getRefCorners(direction, false, currentLayout); - - return (0, _sortBy2.default)(siblings, function (sibling) { - var siblingCorners = SpatialNavigation.getRefCorners(direction, true, sibling.layout); - - var isAdjacentSlice = SpatialNavigation.isAdjacentSlice(refCorners, siblingCorners, isVerticalDirection); - - var primaryAxisFunction = isAdjacentSlice ? SpatialNavigation.getPrimaryAxisDistance : SpatialNavigation.getSecondaryAxisDistance; - - var secondaryAxisFunction = isAdjacentSlice ? SpatialNavigation.getSecondaryAxisDistance : SpatialNavigation.getPrimaryAxisDistance; - - var primaryAxisDistance = primaryAxisFunction(refCorners, siblingCorners, isVerticalDirection); - var secondaryAxisDistance = secondaryAxisFunction(refCorners, siblingCorners, isVerticalDirection); - - /** - * The higher this value is, the less prioritised the candidate is - */ - var totalDistancePoints = primaryAxisDistance * MAIN_COORDINATE_WEIGHT + secondaryAxisDistance; - - /** - * + 1 here is in case of distance is zero, but we still want to apply Adjacent priority weight - */ - var priority = (totalDistancePoints + 1) / (isAdjacentSlice ? ADJACENT_SLICE_WEIGHT : DIAGONAL_SLICE_WEIGHT); - - _this.log('smartNavigate', 'distance (primary, secondary, total weighted) for ' + sibling.focusKey + ' relative to ' + focusKey + ' is', primaryAxisDistance, secondaryAxisDistance, totalDistancePoints); - - _this.log('smartNavigate', 'priority for ' + sibling.focusKey + ' relative to ' + focusKey + ' is', priority); - - if (_this.visualDebugger) { - _this.visualDebugger.drawPoint(siblingCorners.a.x, siblingCorners.a.y, 'yellow', 6); - _this.visualDebugger.drawPoint(siblingCorners.b.x, siblingCorners.b.y, 'yellow', 6); - } - - return priority; - }); - } - }], [{ - key: 'getCutoffCoordinate', - - /** - * Used to determine the coordinate that will be used to filter items that are over the "edge" - */ - value: function getCutoffCoordinate(isVertical, isIncremental, isSibling, layout) { - var itemX = layout.left; - var itemY = layout.top; - var itemWidth = layout.width; - var itemHeight = layout.height; - - var coordinate = isVertical ? itemY : itemX; - var itemSize = isVertical ? itemHeight : itemWidth; - - return isIncremental ? isSibling ? coordinate : coordinate + itemSize : isSibling ? coordinate + itemSize : coordinate; - } - - /** - * Returns two corners (a and b) coordinates that are used as a reference points - * Where "a" is always leftmost and topmost corner, and "b" is rightmost bottommost corner - */ - - }, { - key: 'getRefCorners', - value: function getRefCorners(direction, isSibling, layout) { - var itemX = layout.left; - var itemY = layout.top; - var itemWidth = layout.width; - var itemHeight = layout.height; - - var result = { - a: { - x: 0, - y: 0 - }, - b: { - x: 0, - y: 0 - } - }; - - switch (direction) { - case DIRECTION_UP: - { - var y = isSibling ? itemY + itemHeight : itemY; - - result.a = { - x: itemX, - y: y - }; - - result.b = { - x: itemX + itemWidth, - y: y - }; - - break; - } - - case DIRECTION_DOWN: - { - var _y = isSibling ? itemY : itemY + itemHeight; - - result.a = { - x: itemX, - y: _y - }; - - result.b = { - x: itemX + itemWidth, - y: _y - }; - - break; - } - - case DIRECTION_LEFT: - { - var x = isSibling ? itemX + itemWidth : itemX; - - result.a = { - x: x, - y: itemY - }; - - result.b = { - x: x, - y: itemY + itemHeight - }; - - break; - } - - case DIRECTION_RIGHT: - { - var _x = isSibling ? itemX : itemX + itemWidth; - - result.a = { - x: _x, - y: itemY - }; - - result.b = { - x: _x, - y: itemY + itemHeight - }; - - break; - } - - default: - break; - } - - return result; - } - - /** - * Calculates if the sibling node is intersecting enough with the ref node by the secondary coordinate - */ - - }, { - key: 'isAdjacentSlice', - value: function isAdjacentSlice(refCorners, siblingCorners, isVerticalDirection) { - var refA = refCorners.a, - refB = refCorners.b; - var siblingA = siblingCorners.a, - siblingB = siblingCorners.b; - - var coordinate = isVerticalDirection ? 'x' : 'y'; - - var refCoordinateA = refA[coordinate]; - var refCoordinateB = refB[coordinate]; - var siblingCoordinateA = siblingA[coordinate]; - var siblingCoordinateB = siblingB[coordinate]; - - var thresholdDistance = (refCoordinateB - refCoordinateA) * ADJACENT_SLICE_THRESHOLD; - - var intersectionLength = Math.max(0, Math.min(refCoordinateB, siblingCoordinateB) - Math.max(refCoordinateA, siblingCoordinateA)); - - return intersectionLength >= thresholdDistance; - } - }, { - key: 'getPrimaryAxisDistance', - value: function getPrimaryAxisDistance(refCorners, siblingCorners, isVerticalDirection) { - var refA = refCorners.a; - var siblingA = siblingCorners.a; - - var coordinate = isVerticalDirection ? 'y' : 'x'; - - return Math.abs(siblingA[coordinate] - refA[coordinate]); - } - }, { - key: 'getSecondaryAxisDistance', - value: function getSecondaryAxisDistance(refCorners, siblingCorners, isVerticalDirection) { - var refA = refCorners.a, - refB = refCorners.b; - var siblingA = siblingCorners.a, - siblingB = siblingCorners.b; - - var coordinate = isVerticalDirection ? 'x' : 'y'; - - var refCoordinateA = refA[coordinate]; - var refCoordinateB = refB[coordinate]; - var siblingCoordinateA = siblingA[coordinate]; - var siblingCoordinateB = siblingB[coordinate]; - - var distancesToCompare = []; - - distancesToCompare.push(Math.abs(siblingCoordinateA - refCoordinateA)); - distancesToCompare.push(Math.abs(siblingCoordinateA - refCoordinateB)); - distancesToCompare.push(Math.abs(siblingCoordinateB - refCoordinateA)); - distancesToCompare.push(Math.abs(siblingCoordinateB - refCoordinateB)); - - return Math.min.apply(Math, distancesToCompare); - } - }]); - - function SpatialNavigation() { - _classCallCheck(this, SpatialNavigation); - - /** - * Storage for all focusable components - */ - this.focusableComponents = {}; - - /** - * Storing current focused key - */ - this.focusKey = null; - - /** - * This collection contains focus keys of the elements that are having a child focused - * Might be handy for styling of certain parent components if their child is focused. - */ - this.parentsHavingFocusedChild = []; - - this.enabled = false; - this.nativeMode = false; - this.throttle = 0; - - this.pressedKeys = {}; - - /** - * Flag used to block key events from this service - * @type {boolean} - */ - this.paused = false; - - this.keyDownEventListener = null; - this.keyUpEventListener = null; - this.keyMap = DEFAULT_KEY_MAP; - - this.onKeyEvent = this.onKeyEvent.bind(this); - this.pause = this.pause.bind(this); - this.resume = this.resume.bind(this); - this.setFocus = this.setFocus.bind(this); - this.navigateByDirection = this.navigateByDirection.bind(this); - this.init = this.init.bind(this); - this.setKeyMap = this.setKeyMap.bind(this); - - this.debug = false; - this.visualDebugger = null; - - this.logIndex = 0; - } - - _createClass(SpatialNavigation, [{ - key: 'init', - value: function init() { - var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, - _ref2$debug = _ref2.debug, - debug = _ref2$debug === undefined ? false : _ref2$debug, - _ref2$visualDebug = _ref2.visualDebug, - visualDebug = _ref2$visualDebug === undefined ? false : _ref2$visualDebug, - _ref2$nativeMode = _ref2.nativeMode, - nativeMode = _ref2$nativeMode === undefined ? false : _ref2$nativeMode, - _ref2$throttle = _ref2.throttle, - throttle = _ref2$throttle === undefined ? 0 : _ref2$throttle; - - if (!this.enabled) { - this.enabled = true; - this.nativeMode = nativeMode; - - this.debug = debug; - - if (!this.nativeMode) { - if (Number.isInteger(throttle) && throttle > 0) { - this.throttle = throttle; - } - this.bindEventHandlers(); - if (visualDebug) { - this.visualDebugger = new _visualDebugger2.default(); - this.startDrawLayouts(); - } - } - } - } - }, { - key: 'startDrawLayouts', - value: function startDrawLayouts() { - var _this2 = this; - - var draw = function draw() { - requestAnimationFrame(function () { - _this2.visualDebugger.clearLayouts(); - (0, _forOwn2.default)(_this2.focusableComponents, function (component, focusKey) { - _this2.visualDebugger.drawLayout(component.layout, focusKey, component.parentFocusKey); - }); - draw(); - }); - }; - - draw(); - } - }, { - key: 'destroy', - value: function destroy() { - if (this.enabled) { - this.enabled = false; - this.nativeMode = false; - this.throttle = 0; - this.focusKey = null; - this.parentsHavingFocusedChild = []; - this.focusableComponents = {}; - this.paused = false; - this.keyMap = DEFAULT_KEY_MAP; - - this.unbindEventHandlers(); - } - } - }, { - key: 'getEventType', - value: function getEventType(keyCode) { - return (0, _findKey2.default)(this.getKeyMap(), function (code) { - return keyCode === code; - }); - } - }, { - key: 'bindEventHandlers', - value: function bindEventHandlers() { - var _this3 = this; - - if (typeof window !== "undefined") { - this.keyDownEventListener = function (event) { - if (_this3.paused === true) { - return; - } - - if (_this3.debug) { - _this3.logIndex += 1; - } - - var eventType = _this3.getEventType(event.keyCode); - - if (!eventType) { - return; - } - - _this3.pressedKeys[eventType] = _this3.pressedKeys[eventType] ? _this3.pressedKeys[eventType] + 1 : 1; - - event.preventDefault(); - event.stopPropagation(); - - var details = { - pressedKeys: _this3.pressedKeys - }; - - if (eventType === KEY_ENTER && _this3.focusKey) { - _this3.onEnterPress(details); - - return; - } - - var preventDefaultNavigation = _this3.onArrowPress(eventType, details) === false; - - if (preventDefaultNavigation) { - _this3.log('keyDownEventListener', 'default navigation prevented'); - _this3.visualDebugger && _this3.visualDebugger.clear(); - } else { - _this3.onKeyEvent(event); - } - }; - - // Apply throttle only if the option we got is > 0 to avoid limiting the listener to every animation frame - if (this.throttle) { - this.keyDownEventListener = (0, _throttle2.default)(this.keyDownEventListener.bind(this), this.throttle, THROTTLE_OPTIONS); - } - - // When throttling then make sure to only throttle key down and cancel any queued functions in case of key up - this.keyUpEventListener = function (event) { - var eventType = _this3.getEventType(event.keyCode); - - Reflect.deleteProperty(_this3.pressedKeys, eventType); - - if (_this3.throttle) { - _this3.keyDownEventListener.cancel(); - } - }; - - window.addEventListener('keyup', this.keyUpEventListener); - window.addEventListener('keydown', this.keyDownEventListener); - } - } - }, { - key: 'unbindEventHandlers', - value: function unbindEventHandlers() { - if (typeof window !== "undefined") { - window.removeEventListener('keydown', this.keyDownEventListener); - this.keyDownEventListener = null; - - if (this.throttle) { - window.removeEventListener('keyup', this.keyUpEventListener); - this.keyUpEventListener = null; - } - } - } - }, { - key: 'onEnterPress', - value: function onEnterPress(details) { - var component = this.focusableComponents[this.focusKey]; - - /* Guard against last-focused component being unmounted at time of onEnterPress (e.g due to UI fading out) */ - if (!component) { - this.log('onEnterPress', 'noComponent'); - - return; - } - - /* Suppress onEnterPress if the last-focused item happens to lose its 'focused' status. */ - if (!component.focusable) { - this.log('onEnterPress', 'componentNotFocusable'); - - return; - } - - component.onEnterPressHandler && component.onEnterPressHandler(details); - } - }, { - key: 'onArrowPress', - value: function onArrowPress() { - var component = this.focusableComponents[this.focusKey]; - - /* Guard against last-focused component being unmounted at time of onArrowPress (e.g due to UI fading out) */ - if (!component) { - this.log('onArrowPress', 'noComponent'); - - return undefined; - } - - /* It's okay to navigate AWAY from an item that has lost its 'focused' status, so we don't inspect - * component.focusable. */ - - return component && component.onArrowPressHandler && component.onArrowPressHandler.apply(component, arguments); - } - - /** - * Move focus by direction, if you can't use buttons or focusing by key. - * - * @param {string} direction - * @param {object} details - * - * @example - * navigateByDirection('right') // The focus is moved to right - */ - - }, { - key: 'navigateByDirection', - value: function navigateByDirection(direction) { - var details = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - if (this.paused === true) { - return; - } - - var validDirections = [DIRECTION_DOWN, DIRECTION_UP, DIRECTION_LEFT, DIRECTION_RIGHT]; - - if (validDirections.includes(direction)) { - this.log('navigateByDirection', 'direction', direction); - this.smartNavigate(direction, null, details); - } else { - this.log('navigateByDirection', 'Invalid direction. You passed: `' + direction + '`, but you can use only these: ', validDirections); - } - } - }, { - key: 'onKeyEvent', - value: function onKeyEvent(event) { - this.visualDebugger && this.visualDebugger.clear(); - - var direction = (0, _findKey2.default)(this.getKeyMap(), function (code) { - return event.keyCode === code; - }); - - this.smartNavigate(direction, null, { event: event }); - } - - /** - * This function navigates between siblings OR goes up by the Tree - * Based on the Direction - */ - - }, { - key: 'smartNavigate', - value: function smartNavigate(direction, fromParentFocusKey, details) { - this.log('smartNavigate', 'direction', direction); - this.log('smartNavigate', 'fromParentFocusKey', fromParentFocusKey); - this.log('smartNavigate', 'this.focusKey', this.focusKey); - - var currentComponent = this.focusableComponents[fromParentFocusKey || this.focusKey]; - - this.log('smartNavigate', 'currentComponent', currentComponent ? currentComponent.focusKey : undefined, currentComponent ? currentComponent.node : undefined); - - if (currentComponent) { - var parentFocusKey = currentComponent.parentFocusKey, - focusKey = currentComponent.focusKey, - layout = currentComponent.layout; - - - var isVerticalDirection = direction === DIRECTION_DOWN || direction === DIRECTION_UP; - var isIncrementalDirection = direction === DIRECTION_DOWN || direction === DIRECTION_RIGHT; - - var currentCutoffCoordinate = SpatialNavigation.getCutoffCoordinate(isVerticalDirection, isIncrementalDirection, false, layout); - - /** - * Get only the siblings with the coords on the way of our moving direction - */ - var siblings = (0, _filter2.default)(this.focusableComponents, function (component) { - if (component.parentFocusKey === parentFocusKey && component.focusable) { - var siblingCutoffCoordinate = SpatialNavigation.getCutoffCoordinate(isVerticalDirection, isIncrementalDirection, true, component.layout); - - return isIncrementalDirection ? siblingCutoffCoordinate >= currentCutoffCoordinate : siblingCutoffCoordinate <= currentCutoffCoordinate; - } - - return false; - }); - - if (this.debug) { - this.log('smartNavigate', 'currentCutoffCoordinate', currentCutoffCoordinate); - this.log('smartNavigate', 'siblings', siblings.length + ' elements:', siblings.map(function (sibling) { - return sibling.focusKey; - }).join(', '), siblings.map(function (sibling) { - return sibling.node; - })); - } - - if (this.visualDebugger) { - var refCorners = SpatialNavigation.getRefCorners(direction, false, layout); - - this.visualDebugger.drawPoint(refCorners.a.x, refCorners.a.y); - this.visualDebugger.drawPoint(refCorners.b.x, refCorners.b.y); - } - - var sortedSiblings = this.sortSiblingsByPriority(siblings, layout, direction, focusKey); - - var nextComponent = (0, _first2.default)(sortedSiblings); - - this.log('smartNavigate', 'nextComponent', nextComponent ? nextComponent.focusKey : undefined, nextComponent ? nextComponent.node : undefined); - - if (nextComponent) { - this.setFocus(nextComponent.focusKey, null, details); - } else { - var parentComponent = this.focusableComponents[parentFocusKey]; - - this.saveLastFocusedChildKey(parentComponent, focusKey); - - this.smartNavigate(direction, parentFocusKey, details); - } - } - } - }, { - key: 'saveLastFocusedChildKey', - value: function saveLastFocusedChildKey(component, focusKey) { - if (component) { - this.log('saveLastFocusedChildKey', component.focusKey + ' lastFocusedChildKey set', focusKey); - component.lastFocusedChildKey = focusKey; - } - } - }, { - key: 'log', - value: function log(functionName, debugString) { - if (this.debug) { - var _console; - - for (var _len = arguments.length, rest = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - rest[_key - 2] = arguments[_key]; - } - - (_console = console).log.apply(_console, ['%c' + functionName + '%c' + debugString, 'background: ' + DEBUG_FN_COLORS[this.logIndex % DEBUG_FN_COLORS.length] + '; color: black; padding: 1px 5px;', 'background: #333; color: #BADA55; padding: 1px 5px;'].concat(rest)); - } - } - - /** - * This function tries to determine the next component to Focus - * It's either the target node OR the one down by the Tree if node has children components - * Based on "targetFocusKey" that means the "intended component to focus" - */ - - }, { - key: 'getNextFocusKey', - value: function getNextFocusKey(targetFocusKey) { - var targetComponent = this.focusableComponents[targetFocusKey]; - - /** - * Security check, if component doesn't exist, stay on the same focusKey - */ - if (!targetComponent || this.nativeMode) { - return targetFocusKey; - } - - var children = (0, _filter2.default)(this.focusableComponents, function (component) { - return component.parentFocusKey === targetFocusKey && component.focusable; - }); - - if (children.length > 0) { - var lastFocusedChildKey = targetComponent.lastFocusedChildKey, - preferredChildFocusKey = targetComponent.preferredChildFocusKey; - - - this.log('getNextFocusKey', 'lastFocusedChildKey is', lastFocusedChildKey); - this.log('getNextFocusKey', 'preferredChildFocusKey is', preferredChildFocusKey); - - /** - * First of all trying to focus last focused child - */ - if (lastFocusedChildKey && !targetComponent.forgetLastFocusedChild && this.isParticipatingFocusableComponent(lastFocusedChildKey)) { - this.log('getNextFocusKey', 'lastFocusedChildKey will be focused', lastFocusedChildKey); - - return this.getNextFocusKey(lastFocusedChildKey); - } - - /** - * If there is no lastFocusedChild, trying to focus the preferred focused key - */ - if (preferredChildFocusKey && this.isParticipatingFocusableComponent(preferredChildFocusKey)) { - this.log('getNextFocusKey', 'preferredChildFocusKey will be focused', preferredChildFocusKey); - - return this.getNextFocusKey(preferredChildFocusKey); - } - - /** - * Otherwise, trying to focus something by coordinates - */ - - var _getChildClosestToOri = getChildClosestToOrigin(children), - childKey = _getChildClosestToOri.focusKey; - - this.log('getNextFocusKey', 'childKey will be focused', childKey); - - return this.getNextFocusKey(childKey); - } - - /** - * If no children, just return targetFocusKey back - */ - this.log('getNextFocusKey', 'targetFocusKey', targetFocusKey); - - return targetFocusKey; - } - }, { - key: 'addFocusable', - value: function addFocusable(_ref3) { - var focusKey = _ref3.focusKey, - node = _ref3.node, - parentFocusKey = _ref3.parentFocusKey, - onEnterPressHandler = _ref3.onEnterPressHandler, - onArrowPressHandler = _ref3.onArrowPressHandler, - onBecameFocusedHandler = _ref3.onBecameFocusedHandler, - onBecameBlurredHandler = _ref3.onBecameBlurredHandler, - forgetLastFocusedChild = _ref3.forgetLastFocusedChild, - trackChildren = _ref3.trackChildren, - onUpdateFocus = _ref3.onUpdateFocus, - onUpdateHasFocusedChild = _ref3.onUpdateHasFocusedChild, - preferredChildFocusKey = _ref3.preferredChildFocusKey, - focusable = _ref3.focusable; - - this.focusableComponents[focusKey] = { - focusKey: focusKey, - node: node, - parentFocusKey: parentFocusKey, - onEnterPressHandler: onEnterPressHandler, - onArrowPressHandler: onArrowPressHandler, - onBecameFocusedHandler: onBecameFocusedHandler, - onBecameBlurredHandler: onBecameBlurredHandler, - onUpdateFocus: onUpdateFocus, - onUpdateHasFocusedChild: onUpdateHasFocusedChild, - forgetLastFocusedChild: forgetLastFocusedChild, - trackChildren: trackChildren, - lastFocusedChildKey: null, - preferredChildFocusKey: preferredChildFocusKey, - focusable: focusable, - layout: { - x: 0, - y: 0, - width: 0, - height: 0, - left: 0, - top: 0, - - /** - * Node ref is also duplicated in layout to be reported in onBecameFocused callback - * E.g. used in native environments to lazy-measure the layout on focus - */ - node: node - } - }; - - if (this.nativeMode) { - return; - } - - this.updateLayout(focusKey); - - /** - * If for some reason this component was already focused before it was added, call the update - */ - if (focusKey === this.focusKey) { - this.setFocus(focusKey); - } - } - }, { - key: 'removeFocusable', - value: function removeFocusable(_ref4) { - var focusKey = _ref4.focusKey; - - var componentToRemove = this.focusableComponents[focusKey]; - - if (componentToRemove) { - var parentFocusKey = componentToRemove.parentFocusKey; - - - Reflect.deleteProperty(this.focusableComponents, focusKey); - - var parentComponent = this.focusableComponents[parentFocusKey]; - var isFocused = focusKey === this.focusKey; - - /** - * If the component was stored as lastFocusedChild, clear lastFocusedChildKey from parent - */ - parentComponent && parentComponent.lastFocusedChildKey === focusKey && (parentComponent.lastFocusedChildKey = null); - - if (this.nativeMode) { - return; - } - - /** - * If the component was also focused at this time, focus another one - */ - if (isFocused) { - this.setFocus(parentFocusKey); - } - } - } - }, { - key: 'getNodeLayoutByFocusKey', - value: function getNodeLayoutByFocusKey(focusKey) { - var component = this.focusableComponents[focusKey]; - - if (component) { - return component.layout; - } - - return null; - } - }, { - key: 'setCurrentFocusedKey', - value: function setCurrentFocusedKey(newFocusKey, details) { - if (this.isFocusableComponent(this.focusKey) && newFocusKey !== this.focusKey) { - var oldComponent = this.focusableComponents[this.focusKey]; - var parentComponent = this.focusableComponents[oldComponent.parentFocusKey]; - - this.saveLastFocusedChildKey(parentComponent, this.focusKey); - - oldComponent.onUpdateFocus(false); - oldComponent.onBecameBlurredHandler(this.getNodeLayoutByFocusKey(this.focusKey), details); - } - - this.focusKey = newFocusKey; - - if (this.isFocusableComponent(this.focusKey)) { - var newComponent = this.focusableComponents[this.focusKey]; - - newComponent.onUpdateFocus(true); - newComponent.onBecameFocusedHandler(this.getNodeLayoutByFocusKey(this.focusKey), details); - } - } - }, { - key: 'updateParentsHasFocusedChild', - value: function updateParentsHasFocusedChild(focusKey, details) { - var _this4 = this; - - var parents = []; - - var currentComponent = this.focusableComponents[focusKey]; - - /** - * Recursively iterate the tree up and find all the parents' focus keys - */ - while (currentComponent) { - var _currentComponent = currentComponent, - parentFocusKey = _currentComponent.parentFocusKey; - - - var parentComponent = this.focusableComponents[parentFocusKey]; - - if (parentComponent) { - var currentParentFocusKey = parentComponent.focusKey; - - - parents.push(currentParentFocusKey); - } - - currentComponent = parentComponent; - } - - var parentsToRemoveFlag = (0, _difference2.default)(this.parentsHavingFocusedChild, parents); - var parentsToAddFlag = (0, _difference2.default)(parents, this.parentsHavingFocusedChild); - - (0, _forEach2.default)(parentsToRemoveFlag, function (parentFocusKey) { - var parentComponent = _this4.focusableComponents[parentFocusKey]; - - parentComponent && parentComponent.trackChildren && parentComponent.onUpdateHasFocusedChild(false); - _this4.onIntermediateNodeBecameBlurred(parentFocusKey, details); - }); - - (0, _forEach2.default)(parentsToAddFlag, function (parentFocusKey) { - var parentComponent = _this4.focusableComponents[parentFocusKey]; - - parentComponent && parentComponent.trackChildren && parentComponent.onUpdateHasFocusedChild(true); - _this4.onIntermediateNodeBecameFocused(parentFocusKey, details); - }); - - this.parentsHavingFocusedChild = parents; - } - }, { - key: 'updateParentsLastFocusedChild', - value: function updateParentsLastFocusedChild(focusKey) { - var currentComponent = this.focusableComponents[focusKey]; - - /** - * Recursively iterate the tree up and update all the parent's lastFocusedChild - */ - while (currentComponent) { - var _currentComponent2 = currentComponent, - parentFocusKey = _currentComponent2.parentFocusKey; - - - var parentComponent = this.focusableComponents[parentFocusKey]; - - if (parentComponent) { - this.saveLastFocusedChildKey(parentComponent, currentComponent.focusKey); - } - - currentComponent = parentComponent; - } - } - }, { - key: 'getKeyMap', - value: function getKeyMap() { - return this.keyMap; - } - }, { - key: 'setKeyMap', - value: function setKeyMap(keyMap) { - this.keyMap = _extends({}, this.getKeyMap(), keyMap); - } - }, { - key: 'isFocusableComponent', - value: function isFocusableComponent(focusKey) { - return !!this.focusableComponents[focusKey]; - } - - /** - * Checks whether the focusableComponent is actually participating in spatial navigation (in other words, is a - * 'focusable' focusableComponent). Seems less confusing than calling it isFocusableFocusableComponent() - */ - - }, { - key: 'isParticipatingFocusableComponent', - value: function isParticipatingFocusableComponent(focusKey) { - return this.isFocusableComponent(focusKey) && this.focusableComponents[focusKey].focusable; - } - }, { - key: 'onIntermediateNodeBecameFocused', - value: function onIntermediateNodeBecameFocused(focusKey, details) { - this.isParticipatingFocusableComponent(focusKey) && this.focusableComponents[focusKey].onBecameFocusedHandler(this.getNodeLayoutByFocusKey(focusKey), details); - } - }, { - key: 'onIntermediateNodeBecameBlurred', - value: function onIntermediateNodeBecameBlurred(focusKey, details) { - this.isParticipatingFocusableComponent(focusKey) && this.focusableComponents[focusKey].onBecameBlurredHandler(this.getNodeLayoutByFocusKey(focusKey), details); - } - }, { - key: 'pause', - value: function pause() { - this.paused = true; - } - }, { - key: 'resume', - value: function resume() { - this.paused = false; - } - }, { - key: 'setFocus', - value: function setFocus(focusKey, overwriteFocusKey) { - var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - - if (!this.enabled) { - return; - } - - var targetFocusKey = overwriteFocusKey || focusKey; - - this.log('setFocus', 'targetFocusKey', targetFocusKey); - - var lastFocusedKey = this.focusKey; - var newFocusKey = this.getNextFocusKey(targetFocusKey); - - this.log('setFocus', 'newFocusKey', newFocusKey); - - this.setCurrentFocusedKey(newFocusKey, details); - this.updateParentsHasFocusedChild(newFocusKey, details); - this.updateParentsLastFocusedChild(lastFocusedKey); - - if (!this.nativeMode) { - this.updateAllLayouts(); - } - } - }, { - key: 'updateAllLayouts', - value: function updateAllLayouts() { - var _this5 = this; - - if (this.nativeMode) { - return; - } - - (0, _forOwn2.default)(this.focusableComponents, function (component, focusKey) { - _this5.updateLayout(focusKey); - }); - } - }, { - key: 'updateLayout', - value: function updateLayout(focusKey) { - var component = this.focusableComponents[focusKey]; - - if (!component || this.nativeMode) { - return; - } - - var node = component.node; - - - (0, _measureLayout2.default)(node, function (x, y, width, height, left, top) { - component.layout = { - x: x, - y: y, - width: width, - height: height, - left: left, - top: top, - node: node - }; - }); - } - }, { - key: 'updateFocusable', - value: function updateFocusable(focusKey, _ref5) { - var node = _ref5.node, - preferredChildFocusKey = _ref5.preferredChildFocusKey, - focusable = _ref5.focusable; - - if (this.nativeMode) { - return; - } - - var component = this.focusableComponents[focusKey]; - - if (component) { - component.preferredChildFocusKey = preferredChildFocusKey; - component.focusable = focusable; - - if (node) { - component.node = node; - } - } - } - }, { - key: 'isNativeMode', - value: function isNativeMode() { - return this.nativeMode; - } - }]); - - return SpatialNavigation; -}(); - -/** - * Export singleton - */ - - -exports.default = new SpatialNavigation(); \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/visualDebugger.js b/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/visualDebugger.js deleted file mode 100644 index 2e9daceb6b..0000000000 --- a/packages/rnv/pluginTemplates/@noriginmedia/react-spatial-navigation/overrides/dist/visualDebugger.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var hasDOM = typeof window !== "undefined" && window.document; - -var WIDTH = hasDOM ? window.innerWidth : 0; -var HEIGHT = hasDOM ? window.innerHeight : 0; - -var VisualDebugger = function () { - function VisualDebugger() { - _classCallCheck(this, VisualDebugger); - - if (hasDOM) { - this.debugCtx = VisualDebugger.createCanvas('sn-debug', 1010); - this.layoutsCtx = VisualDebugger.createCanvas('sn-layouts', 1000); - } - } - - _createClass(VisualDebugger, [{ - key: 'clear', - value: function clear() { - if (!hasDOM) return; - this.debugCtx.clearRect(0, 0, WIDTH, HEIGHT); - } - }, { - key: 'clearLayouts', - value: function clearLayouts() { - if (!hasDOM) return; - this.layoutsCtx.clearRect(0, 0, WIDTH, HEIGHT); - } - }, { - key: 'drawLayout', - value: function drawLayout(layout, focusKey, parentFocusKey) { - if (!hasDOM) return; - this.layoutsCtx.strokeStyle = 'green'; - this.layoutsCtx.strokeRect(layout.left, layout.top, layout.width, layout.height); - this.layoutsCtx.font = '8px monospace'; - this.layoutsCtx.fillStyle = 'red'; - this.layoutsCtx.fillText(focusKey, layout.left, layout.top + 10); - this.layoutsCtx.fillText(parentFocusKey, layout.left, layout.top + 25); - this.layoutsCtx.fillText('left: ' + layout.left, layout.left, layout.top + 40); - this.layoutsCtx.fillText('top: ' + layout.top, layout.left, layout.top + 55); - } - }, { - key: 'drawPoint', - value: function drawPoint(x, y) { - if (!hasDOM) return; - var color = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'blue'; - var size = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 10; - - this.debugCtx.strokeStyle = color; - this.debugCtx.lineWidth = 3; - this.debugCtx.strokeRect(x - size / 2, y - size / 2, size, size); - } - }], [{ - key: 'createCanvas', - value: function createCanvas(id, zIndex) { - var canvas = document.querySelector('#' + id) || document.createElement('canvas'); - - canvas.setAttribute('id', id); - - var ctx = canvas.getContext('2d'); - - canvas.style = 'position: fixed; top: 0; left: 0; z-index: ' + zIndex; - - document.body.appendChild(canvas); - - canvas.width = WIDTH; - canvas.height = HEIGHT; - - return ctx; - } - }]); - - return VisualDebugger; -}(); - -exports.default = VisualDebugger; \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@react-native-community/datetimepicker/overrides/src/DateTimePickerAndroid.android.js b/packages/rnv/pluginTemplates/@react-native-community/datetimepicker/overrides/src/DateTimePickerAndroid.android.js new file mode 100644 index 0000000000..52f2894482 --- /dev/null +++ b/packages/rnv/pluginTemplates/@react-native-community/datetimepicker/overrides/src/DateTimePickerAndroid.android.js @@ -0,0 +1,129 @@ +/** + * @format + * @flow strict-local + */ +import invariant from 'invariant'; +import { + ANDROID_DISPLAY, + ANDROID_MODE, DATE_SET_ACTION, DISMISS_ACTION, + NEUTRAL_BUTTON_ACTION, TIME_SET_ACTION +} from './constants'; + +import { processColor } from 'react-native'; +import { + getOpenPicker, + timeZoneOffsetDateSetter, + validateAndroidProps +} from './androidUtils'; +import { + createDateTimeSetEvtParams, + createDismissEvtParams, + createNeutralEvtParams +} from './eventCreators'; +import pickers from './picker'; +import type { AndroidNativeProps } from './types'; + +function open(props: AndroidNativeProps) { + const { + mode = ANDROID_MODE.date, + display, + value: originalValue, + is24Hour, + minimumDate, + maximumDate, + minuteInterval, + timeZoneOffsetInMinutes, + onChange, + onError, + positiveButton, + negativeButton, + neutralButton, + neutralButtonLabel, + positiveButtonLabel, + negativeButtonLabel, + } = props; + validateAndroidProps(props); + invariant(originalValue, 'A date or time must be specified as `value` prop.'); + + const valueTimestamp = originalValue.getTime(); + const openPicker = getOpenPicker(mode); + + const presentPicker = async () => { + try { + const dialogButtons = { + positive: { + label: positiveButtonLabel, + ...positiveButton, + textColor: processColor(positiveButton?.textColor), + }, + neutral: { + label: neutralButtonLabel, + ...neutralButton, + textColor: processColor(neutralButton?.textColor), + }, + negative: { + label: negativeButtonLabel, + ...negativeButton, + textColor: processColor(negativeButton?.textColor), + }, + }; + + const displayOverride = + display === ANDROID_DISPLAY.spinner + ? ANDROID_DISPLAY.spinner + : ANDROID_DISPLAY.default; + const {action, day, month, year, minute, hour} = await openPicker({ + value: valueTimestamp, + display: displayOverride, + is24Hour, + minimumDate, + maximumDate, + minuteInterval, + timeZoneOffsetInMinutes, + dialogButtons, + }); + + switch (action) { + case DATE_SET_ACTION: { + let date = new Date(valueTimestamp); + date.setFullYear(year, month, day); + date = timeZoneOffsetDateSetter(date, timeZoneOffsetInMinutes); + const [event] = createDateTimeSetEvtParams(date); + onChange?.(event, date); + break; + } + + case TIME_SET_ACTION: { + let date = new Date(valueTimestamp); + date.setHours(hour, minute); + date = timeZoneOffsetDateSetter(date, timeZoneOffsetInMinutes); + const [event] = createDateTimeSetEvtParams(date); + onChange?.(event, date); + break; + } + + case NEUTRAL_BUTTON_ACTION: { + const [event] = createNeutralEvtParams(originalValue); + onChange?.(event, originalValue); + break; + } + case DISMISS_ACTION: + default: { + const [event] = createDismissEvtParams(originalValue); + onChange?.(event, originalValue); + break; + } + } + } catch (error) { + onError && onError(error); + } + }; + presentPicker(); +} + +function dismiss(mode) { + // $FlowFixMe - `AbstractComponent` [1] is not an instance type. + return pickers[mode]?.dismiss(); +} + +export const DateTimePickerAndroid = {open, dismiss}; diff --git a/packages/rnv/pluginTemplates/@react-native-firebase/app/overrides/RNFBApp.podspec b/packages/rnv/pluginTemplates/@react-native-firebase/app/overrides/RNFBApp.podspec new file mode 100644 index 0000000000..3824c58d2f --- /dev/null +++ b/packages/rnv/pluginTemplates/@react-native-firebase/app/overrides/RNFBApp.podspec @@ -0,0 +1,32 @@ +require 'json' +require './firebase_json' +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +firebase_sdk_version = package['sdkVersions']['ios']['firebase'] || '~> 6.25.0' + +Pod::Spec.new do |s| + s.name = "RNFBApp" + s.version = package["version"] + s.description = package["description"] + s.summary = <<-DESC + A well tested feature rich Firebase implementation for React Native, supporting iOS & Android. + DESC + s.homepage = "http://invertase.io/oss/react-native-firebase" + s.license = package['license'] + s.authors = "Invertase Limited" + s.source = { :git => "https://github.com/invertase/react-native-firebase.git", :tag => "v#{s.version}" } + s.social_media_url = 'http://twitter.com/invertaseio' + s.platforms = { :ios => "10.0", :tvos => "9.2" } + s.source_files = "ios/**/*.{h,m}" + + # React Native dependencies + s.dependency 'React' + + # Firebase dependencies + s.dependency 'Firebase/CoreOnly', firebase_sdk_version + s.subspec 'Crashlytics' do |cs| + cs.dependency 'Fabric' + cs.dependency 'Crashlytics' + end + + s.static_framework = true +end diff --git a/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerItemList.tsx b/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerItemList.tsx new file mode 100644 index 0000000000..9d3022df7d --- /dev/null +++ b/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerItemList.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { + CommonActions, + DrawerActions, + DrawerNavigationState, + useLinkBuilder, +} from '@react-navigation/native'; +import DrawerItem from './DrawerItem'; +import type { + DrawerNavigationHelpers, + DrawerDescriptorMap, + DrawerContentOptions, +} from '../types'; + +type Props = Omit & { + state: DrawerNavigationState; + navigation: DrawerNavigationHelpers; + descriptors: DrawerDescriptorMap; +}; + +/** + * Component that renders the navigation list in the drawer. + */ +export default function DrawerItemList({ + state, + navigation, + descriptors, + activeTintColor, + inactiveTintColor, + activeBackgroundColor, + inactiveBackgroundColor, + itemStyle, + labelStyle, +}: Props) { + const buildLink = useLinkBuilder(); + + return (state.routes.map((route, i) => { + const focused = i === state.index; + const { title, drawerLabel, drawerIcon } = descriptors[route.key].options; + + return ( + { + navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); + + navigation.dispatch({ + ...(focused + ? DrawerActions.closeDrawer() + : CommonActions.navigate(route.name)), + target: state.key, + }); + }} + /> + ); + }) as React.ReactNode) as React.ReactElement; +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerView.tsx b/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerView.tsx new file mode 100644 index 0000000000..e849d0a983 --- /dev/null +++ b/packages/rnv/pluginTemplates/@react-navigation/drawer/overrides@5/src/views/DrawerView.tsx @@ -0,0 +1,248 @@ +import * as React from 'react'; +import { + View, + StyleSheet, + I18nManager, + Platform, + BackHandler, + NativeEventSubscription, +} from 'react-native'; +// eslint-disable-next-line import/no-unresolved +import { ScreenContainer } from 'react-native-screens'; +import { + NavigationHelpersContext, + DrawerNavigationState, + DrawerActions, + useTheme, +} from '@react-navigation/native'; + +import { GestureHandlerRootView } from './GestureHandler'; +import SafeAreaProviderCompat from './SafeAreaProviderCompat'; +import ResourceSavingScene from './ResourceSavingScene'; +import DrawerContent from './DrawerContent'; +import Drawer from './Drawer'; +import DrawerOpenContext from '../utils/DrawerOpenContext'; +import DrawerPositionContext from '../utils/DrawerPositionContext'; +import useWindowDimensions from '../utils/useWindowDimensions'; +import type { + DrawerDescriptorMap, + DrawerNavigationConfig, + DrawerNavigationHelpers, + DrawerContentComponentProps, +} from '../types'; + +type Props = DrawerNavigationConfig & { + state: DrawerNavigationState; + navigation: DrawerNavigationHelpers; + descriptors: DrawerDescriptorMap; +}; + +const getDefaultDrawerWidth = ({ + height, + width, +}: { + height: number; + width: number; +}) => { + /* + * Default drawer width is screen width - header height + * with a max width of 280 on mobile and 320 on tablet + * https://material.io/guidelines/patterns/navigation-drawer.html + */ + const smallerAxisSize = Math.min(height, width); + const isLandscape = width > height; + const isTablet = smallerAxisSize >= 600; + const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56; + const maxWidth = isTablet ? 320 : 280; + + return Math.min(smallerAxisSize - appBarHeight, maxWidth); +}; + +const GestureHandlerWrapper = GestureHandlerRootView ?? View; + +/** + * Component that renders the drawer. + */ +export default function DrawerView({ + state, + navigation, + descriptors, + lazy = true, + drawerContent = (props: DrawerContentComponentProps) => ( + + ), + drawerView, + drawerPosition = I18nManager.isRTL ? 'right' : 'left', + keyboardDismissMode = 'on-drag', + overlayColor = 'rgba(0, 0, 0, 0.5)', + drawerType = 'front', + hideStatusBar = false, + statusBarAnimation = 'slide', + drawerContentOptions, + drawerStyle, + edgeWidth, + gestureHandlerProps, + minSwipeDistance, + sceneContainerStyle, +}: Props) { + const [loaded, setLoaded] = React.useState([state.index]); + const dimensions = useWindowDimensions(); + + const { colors } = useTheme(); + + const isDrawerOpen = state.history.some((it) => it.type === 'drawer'); + + const handleDrawerOpen = React.useCallback(() => { + navigation.dispatch({ + ...DrawerActions.openDrawer(), + target: state.key, + }); + }, [navigation, state.key]); + + const handleDrawerClose = React.useCallback(() => { + navigation.dispatch({ + ...DrawerActions.closeDrawer(), + target: state.key, + }); + }, [navigation, state.key]); + + React.useEffect(() => { + if (isDrawerOpen) { + navigation.emit({ type: 'drawerOpen' }); + } else { + navigation.emit({ type: 'drawerClose' }); + } + }, [isDrawerOpen, navigation]); + + React.useEffect(() => { + let subscription: NativeEventSubscription | undefined; + + if (isDrawerOpen) { + // We only add the subscription when drawer opens + // This way we can make sure that the subscription is added as late as possible + // This will make sure that our handler will run first when back button is pressed + subscription = BackHandler.addEventListener('hardwareBackPress', () => { + handleDrawerClose(); + + return true; + }); + } + + return () => subscription?.remove(); + }, [handleDrawerClose, isDrawerOpen, navigation, state.key]); + + if (!loaded.includes(state.index)) { + setLoaded([...loaded, state.index]); + } + + const renderNavigationView = ({ progress }: any) => { + return ( + + {drawerContent({ + ...drawerContentOptions, + progress: progress, + state: state, + navigation: navigation, + descriptors: descriptors, + })} + + ); + }; + + const renderContent = () => { + return ( + + {state.routes.map((route, index) => { + const descriptor = descriptors[route.key]; + const { unmountOnBlur } = descriptor.options; + const isFocused = state.index === index; + + if (unmountOnBlur && !isFocused) { + return null; + } + + if (lazy && !loaded.includes(index) && !isFocused) { + // Don't render a screen if we've never navigated to it + return null; + } + + return ( + + {descriptor.render()} + + ); + })} + + ); + }; + + const activeKey = state.routes[state.index].key; + const { gestureEnabled, swipeEnabled } = descriptors[activeKey].options; + + const drawerViewProps = { + isDrawerOpen, + gestureEnabled, + swipeEnabled, + handleDrawerOpen, + handleDrawerClose, + gestureHandlerProps, + drawerType, + drawerPosition, + sceneContainerStyle: [ + { backgroundColor: colors.background }, + sceneContainerStyle, + ], + drawerStyle: [ + { + width: getDefaultDrawerWidth(dimensions), + backgroundColor: colors.card, + }, + drawerType === 'permanent' && + (drawerPosition === 'left' + ? { + borderRightColor: colors.border, + borderRightWidth: StyleSheet.hairlineWidth, + } + : { + borderLeftColor: colors.border, + borderLeftWidth: StyleSheet.hairlineWidth, + }), + drawerStyle, + ], + overlayStyle: { + backgroundColor: overlayColor + }, + swipeEdgeWidth: edgeWidth, + swipeDistanceThreshold: minSwipeDistance, + hideStatusBar, + statusBarAnimation, + renderDrawerContent: renderNavigationView, + renderSceneContent: renderContent, + keyboardDismissMode, + dimensions + }; + + return ( + + + + + {drawerView ? drawerView({...drawerViewProps}) : ( + + )} + + + + + ); +} + +const styles = StyleSheet.create({ + content: { + flex: 1, + }, +}); diff --git a/packages/rnv/pluginTemplates/@sentry/react-native/overrides/ios/RNSentry.m b/packages/rnv/pluginTemplates/@sentry/react-native/overrides/ios/RNSentry.m new file mode 100644 index 0000000000..fcd5404e3a --- /dev/null +++ b/packages/rnv/pluginTemplates/@sentry/react-native/overrides/ios/RNSentry.m @@ -0,0 +1,359 @@ +#import "RNSentry.h" + +#if __has_include() +#import +#else +#import "RCTConvert.h" +#endif + +#import +#import + +@interface SentrySDK (RNSentry) + ++ (void)captureEnvelope:(SentryEnvelope *)envelope; + ++ (void)storeEnvelope:(SentryEnvelope *)envelope; + +@end + +static bool didFetchAppStart; + +@implementation RNSentry { + bool sentHybridSdkDidBecomeActive; + SentryOptions *sentryOptions; +} + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + ++ (BOOL)requiresMainQueueSetup { + return YES; +} + +RCT_EXPORT_MODULE() + +- (NSDictionary *)constantsToExport +{ + return @{@"nativeClientAvailable": @YES, @"nativeTransport": @YES}; +} + +RCT_EXPORT_METHOD(initNativeSdk:(NSDictionary *_Nonnull)options + resolve:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true; + + NSError *error = nil; + + SentryBeforeSendEventCallback beforeSend = ^SentryEvent*(SentryEvent *event) { + // We don't want to send an event after startup that came from a Unhandled JS Exception of react native + // Because we sent it already before the app crashed. + if (nil != event.exceptions.firstObject.type && + [event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) { + NSLog(@"Unhandled JS Exception"); + return nil; + } + + [self setEventOriginTag:event]; + + return event; + }; + + [options setValue:beforeSend forKey:@"beforeSend"]; + + sentryOptions = [[SentryOptions alloc] initWithDict:options didFailWithError:&error]; + if (error) { + reject(@"SentryReactNative", error.localizedDescription, error); + return; + } + [SentrySDK startWithOptionsObject:sentryOptions]; + +// // If the app is active/in foreground, and we have not sent the SentryHybridSdkDidBecomeActive notification, send it. +#if !TARGET_OS_OSX + if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive && !sentHybridSdkDidBecomeActive && sentryOptions.enableAutoSessionTracking) { + [[NSNotificationCenter defaultCenter] + postNotificationName:@"SentryHybridSdkDidBecomeActive" + object:nil]; + + sentHybridSdkDidBecomeActive = true; + } +#endif + + + + resolve(@YES); +} + +- (void)setEventOriginTag:(SentryEvent *)event { + if (event.sdk != nil) { + NSString *sdkName = event.sdk[@"name"]; + + // If the event is from react native, it gets set there and we do not handle + // it here. + if ([sdkName isEqualToString:@"sentry.cocoa"]) { + [self setEventEnvironmentTag:event origin:@"ios" environment:@"native"]; + } + } +} + +- (void)setEventEnvironmentTag:(SentryEvent *)event + origin:(NSString *)origin + environment:(NSString *)environment { + NSMutableDictionary *newTags = [NSMutableDictionary new]; + + if (nil != event.tags && [event.tags count] > 0) { + [newTags addEntriesFromDictionary:event.tags]; + } + if (nil != origin) { + [newTags setValue:origin forKey:@"event.origin"]; + } + if (nil != environment) { + [newTags setValue:environment forKey:@"event.environment"]; + } + + event.tags = newTags; +} + +RCT_EXPORT_METHOD(fetchNativeSdkInfo:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + if (sentryOptions == nil) { + return reject(@"SentryReactNative",@"Called fetchNativeSdkInfo without initializing the SDK first.", nil); + } + + resolve(@{ + @"name": sentryOptions.sdkInfo.name, + @"version": sentryOptions.sdkInfo.version + }); +} + +RCT_EXPORT_METHOD(fetchNativeDeviceContexts:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + NSLog(@"Bridge call to: deviceContexts"); + // Temp work around until sorted out this API in sentry-cocoa. + // TODO: If the callback isnt' executed the promise wouldn't be resolved. + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + NSDictionary *serializedScope = [scope serialize]; + // Scope serializes as 'context' instead of 'contexts' as it does for the event. + NSDictionary *contexts = [serializedScope valueForKey:@"context"]; +#if DEBUG + NSData *data = [NSJSONSerialization dataWithJSONObject:contexts options:0 error:nil]; + NSString *debugContext = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSLog(@"Contexts: %@", debugContext); +#endif + resolve(contexts); + }]; +} + +RCT_EXPORT_METHOD(fetchNativeAppStart:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + + SentryAppStartMeasurement *appStartMeasurement = PrivateSentrySDKOnly.appStartMeasurement; + + if (appStartMeasurement == nil) { + resolve(nil); + } else { + BOOL isColdStart = appStartMeasurement.type == SentryAppStartTypeCold; + + resolve(@{ + @"isColdStart": [NSNumber numberWithBool:isColdStart], + @"appStartTime": [NSNumber numberWithDouble:(appStartMeasurement.appStartTimestamp.timeIntervalSince1970 * 1000)], + @"didFetchAppStart": [NSNumber numberWithBool:didFetchAppStart], + }); + + } + + // This is always set to true, as we would only allow an app start fetch to only happen once + // in the case of a JS bundle reload, we do not want it to be instrumented again. + didFetchAppStart = true; +} + +#if !TARGET_OS_OSX +RCT_EXPORT_METHOD(fetchNativeFrames:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + + if (PrivateSentrySDKOnly.isFramesTrackingRunning) { + SentryScreenFrames *frames = PrivateSentrySDKOnly.currentScreenFrames; + + if (frames == nil) { + resolve(nil); + } + + resolve(@{ + @"totalFrames": [NSNumber numberWithLong:frames.total], + @"frozenFrames": [NSNumber numberWithLong:frames.frozen], + @"slowFrames": [NSNumber numberWithLong:frames.slow], + }); + } else { + resolve(nil); + } +} +#endif + +RCT_EXPORT_METHOD(fetchNativeRelease:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary]; + resolve(@{ + @"id": infoDict[@"CFBundleIdentifier"], + @"version": infoDict[@"CFBundleShortVersionString"], + @"build": infoDict[@"CFBundleVersion"], + }); +} + +RCT_EXPORT_METHOD(captureEnvelope:(NSDictionary * _Nonnull)envelopeDict + resolve:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + if ([NSJSONSerialization isValidJSONObject:envelopeDict]) { + SentrySdkInfo *sdkInfo = [[SentrySdkInfo alloc] initWithDict:envelopeDict[@"header"]]; + SentryId *eventId = [[SentryId alloc] initWithUUIDString:envelopeDict[@"header"][@"event_id"]]; + SentryEnvelopeHeader *envelopeHeader = [[SentryEnvelopeHeader alloc] initWithId:eventId sdkInfo:sdkInfo traceState:nil]; + + NSError *error; + NSData *envelopeItemData = [NSJSONSerialization dataWithJSONObject:envelopeDict[@"payload"] options:0 error:&error]; + if (nil != error) { + reject(@"SentryReactNative", @"Cannot serialize event", error); + } + + NSString *itemType = envelopeDict[@"payload"][@"type"]; + if (itemType == nil) { + // Default to event type. + itemType = @"event"; + } + + SentryEnvelopeItemHeader *envelopeItemHeader = [[SentryEnvelopeItemHeader alloc] initWithType:itemType length:envelopeItemData.length]; + SentryEnvelopeItem *envelopeItem = [[SentryEnvelopeItem alloc] initWithHeader:envelopeItemHeader data:envelopeItemData]; + + SentryEnvelope *envelope = [[SentryEnvelope alloc] initWithHeader:envelopeHeader singleItem:envelopeItem]; + + #if DEBUG + [SentrySDK captureEnvelope:envelope]; + #else + if ([envelopeDict[@"payload"][@"level"] isEqualToString:@"fatal"]) { + // Storing to disk happens asynchronously with captureEnvelope + [SentrySDK storeEnvelope:envelope]; + } else { + [SentrySDK captureEnvelope:envelope]; + } + #endif + resolve(@YES); + } else { + reject(@"SentryReactNative", @"Cannot serialize event", nil); + } +} + +RCT_EXPORT_METHOD(setUser:(NSDictionary *)user + otherUserKeys:(NSDictionary *)otherUserKeys +) +{ + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + if (nil == user && nil == otherUserKeys) { + [scope setUser:nil]; + } else { + SentryUser* userInstance = [[SentryUser alloc] init]; + + if (nil != user) { + [userInstance setUserId:user[@"id"]]; + [userInstance setEmail:user[@"email"]]; + [userInstance setUsername:user[@"username"]]; + } + + if (nil != otherUserKeys) { + [userInstance setData:otherUserKeys]; + } + + [scope setUser:userInstance]; + } + }]; +} + +RCT_EXPORT_METHOD(addBreadcrumb:(NSDictionary *)breadcrumb) +{ + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + SentryBreadcrumb* breadcrumbInstance = [[SentryBreadcrumb alloc] init]; + + NSString * levelString = breadcrumb[@"level"]; + SentryLevel sentryLevel; + if ([levelString isEqualToString:@"fatal"]) { + sentryLevel = kSentryLevelFatal; + } else if ([levelString isEqualToString:@"warning"]) { + sentryLevel = kSentryLevelWarning; + } else if ([levelString isEqualToString:@"info"]) { + sentryLevel = kSentryLevelInfo; + } else if ([levelString isEqualToString:@"debug"]) { + sentryLevel = kSentryLevelDebug; + } else { + sentryLevel = kSentryLevelError; + } + [breadcrumbInstance setLevel:sentryLevel]; + + [breadcrumbInstance setCategory:breadcrumb[@"category"]]; + + [breadcrumbInstance setType:breadcrumb[@"type"]]; + + [breadcrumbInstance setMessage:breadcrumb[@"message"]]; + + [breadcrumbInstance setData:breadcrumb[@"data"]]; + + [scope addBreadcrumb:breadcrumbInstance]; + }]; +} + +RCT_EXPORT_METHOD(clearBreadcrumbs) { + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + [scope clearBreadcrumbs]; + }]; +} + +RCT_EXPORT_METHOD(setExtra:(NSString *)key + extra:(NSString *)extra +) +{ + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + [scope setExtraValue:extra forKey:key]; + }]; +} + +RCT_EXPORT_METHOD(setContext:(NSString *)key + context:(NSDictionary *)context +) +{ + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + [scope setContextValue:context forKey:key]; + }]; +} + +RCT_EXPORT_METHOD(setTag:(NSString *)key + value:(NSString *)value +) +{ + [SentrySDK configureScope:^(SentryScope * _Nonnull scope) { + [scope setTagValue:value forKey:key]; + }]; +} + +RCT_EXPORT_METHOD(crash) +{ + [SentrySDK crash]; +} + +RCT_EXPORT_METHOD(closeNativeSdk:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [SentrySDK close]; + resolve(@YES); +} + +RCT_EXPORT_METHOD(disableNativeFramesTracking) +{ + // Do nothing on iOS, this bridge method only has an effect on android. +} + +@end diff --git a/packages/rnv/pluginTemplates/detox/builds/android@test/app/src/androidTest/java/renative/helloworld/DetoxTest.java b/packages/rnv/pluginTemplates/detox/builds/android@test/app/src/androidTest/java/renative/helloworld/DetoxTest.java new file mode 100644 index 0000000000..5840999beb --- /dev/null +++ b/packages/rnv/pluginTemplates/detox/builds/android@test/app/src/androidTest/java/renative/helloworld/DetoxTest.java @@ -0,0 +1,24 @@ +package {{configProps.id}}; + +import com.wix.detox.Detox; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class DetoxTest { + + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); + + @Test + public void runDetoxTests() { + Detox.runTests(mActivityRule); + } +} diff --git a/packages/rnv/pluginTemplates/detox/overrides/android/detox/build.gradle b/packages/rnv/pluginTemplates/detox/overrides/android/detox/build.gradle new file mode 100644 index 0000000000..5432716818 --- /dev/null +++ b/packages/rnv/pluginTemplates/detox/overrides/android/detox/build.gradle @@ -0,0 +1,140 @@ +// Override added because to bump up kotlin version to 1.3.30 for some kotlin deps _kotlinVersionForCompliancy +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +def _ext = rootProject.ext +def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion : 29 +def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 29 +def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '29.0.3' +def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 18 +def _kotlinVersion = _ext.has('detoxKotlinVersion') ? _ext.detoxKotlinVersion : '1.2.0' +def _kotlinStdlib = _ext.has('detoxKotlinStdlib') ? _ext.detoxKotlinStdlib : 'kotlin-stdlib-jdk8' +def _kotlinVersionForCompliancy = '1.3.30' + +android { + compileSdkVersion _compileSdkVersion + buildToolsVersion _buildToolsVersion + defaultConfig { + minSdkVersion _minSdkVersion + targetSdkVersion _targetSdkVersion + versionCode 1 + versionName '1.0' + + consumerProguardFiles 'proguard-rules.pro' + } + + testOptions { + unitTests.includeAndroidResources = true + unitTests.returnDefaultValues = true + + unitTests.all { t -> + reports { + html.enabled true + } + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + } + afterSuite { desc, result -> + if (!desc.parent) { // will match the outermost suite + def output = " ${result.resultType} (${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped) " + def repeatLength = output.length() + println '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) + '\n' + + println "see report at file://${t.reports.html.destination}/index.html" + } + } + } + } + + packagingOptions { + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/NOTICE' + exclude 'META-INF/LICENSE' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } + + lintOptions { + abortOnError false + } +} + +// In a nutshell: +// "The api configuration should be used to declare dependencies which are exported by the library API, whereas the +// implementation configuration should be used to declare dependencies which are internal to the component." +// --> https://docs.gradle.org/5.5/userguide/java_library_plugin.html + +// Fundamental deps. +dependencies { + implementation "org.jetbrains.kotlin:$_kotlinStdlib:$_kotlinVersionForCompliancy" + + // noinspection GradleDynamicVersion + compileOnly 'com.facebook.react:react-native:+' +} + +// androidx.test deps. +// All are aligned with this release: https://developer.android.com/jetpack/androidx/releases/test#1.2.0 +dependencies { + + // Versions are in-sync with the 'androidx-test-1.2.0' release/tag of the android-test github repo, + // used by the Detox generator. See https://github.com/android/android-test/releases/tag/androidx-test-1.2.0 + // Important: Should remain so when generator tag is replaced! + api('androidx.test.espresso:espresso-core:3.2.0') { // Needed all across Detox but also makes Espresso seamlessly provided to Detox users with hybrid apps/E2E-tests. + exclude group: 'com.google.code.findbugs', module: 'jsr305' + } + api 'androidx.test:rules:1.2.0' // Needed because of ActivityTestRule. Needed by users *and* internally used by Detox. + api 'androidx.test.ext:junit:1.1.1' // Needed so as to seamlessly provide AndroidJUnit4 to Detox users. Depends on junit core. + + // Version is the latest; Cannot sync with the Github repo (e.g. android/android-test) because the androidx + // packaging version of associated classes is simply not there... + api 'androidx.test.uiautomator:uiautomator:2.2.0' // Needed by Detox but also makes UIAutomator seamlessly provided to Detox users with hybrid apps/E2E-tests. +} + +// Third-party deps. +dependencies { + implementation 'org.apache.commons:commons-lang3:3.7' // Needed by invoke. Warning: Upgrading to newer versions is not seamless. + implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0' +} + +// Unit-testing deps. +dependencies { + // noinspection GradleDynamicVersion + testImplementation 'com.facebook.react:react-native:+' + testImplementation 'org.json:json:20140107' + testImplementation 'junit:junit:4.13' + testImplementation 'org.assertj:assertj-core:3.16.1' + testImplementation "org.jetbrains.kotlin:kotlin-test:$_kotlinVersion" + testImplementation 'org.apache.commons:commons-io:1.3.2' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.robolectric:robolectric:4.3.1' +} + +// Spek (https://spekframework.org/setup-android) +if (rootProject.hasProperty('isOfficialDetoxLib') || + rootProject.hasProperty('isOfficialDetoxApp')) { + apply plugin: 'de.mannodermaus.android-junit5' + + android { + testOptions { + junitPlatform { + filters { + engines { + include 'spek2' + } + } + } + } + } + + dependencies { + testImplementation 'org.spekframework.spek2:spek-dsl-jvm:2.0.12' + testImplementation 'org.spekframework.spek2:spek-runner-junit5:2.0.12' + testImplementation "org.jetbrains.kotlin:kotlin-reflect:$_kotlinVersionForCompliancy" + } +} + +// Enable publishing +if (rootProject.hasProperty('isOfficialDetoxLib')) { + apply from: './detox-publishing.gradle' +} diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/entries.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/entries.js deleted file mode 100644 index 800a58d9aa..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/entries.js +++ /dev/null @@ -1,159 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.createPagesMapping = createPagesMapping; -exports.createEntrypoints = createEntrypoints; -var _chalk = _interopRequireDefault( require( "chalk" ) ); -var _path = require( "path" ); -var _querystring = require( "querystring" ); -var _constants = require( "../lib/constants" ); - -var _config = require( "../next-server/server/config" ); -var _normalizePagePath = require( "../next-server/server/normalize-page-path" ); -var _log = require( "./output/log" ); - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} - - - - -function createPagesMapping( - pagePaths, - extensions ) { - const previousPages = {}; - const pages = pagePaths.reduce( - ( result, pagePath ) => { - let page = `${pagePath. - replace( new RegExp( `\\.+(${extensions.join( '|' )})$` ), '' ). - replace( /\\/g, '/' )}`.replace( /\/index$/, '' ); - page = page === '/index' ? '/' : page; - - const pageKey = page === '' ? '/' : page; - - if ( pageKey in result ) { - ( 0, _log.warn )( - `Duplicate page detected. ${_chalk.default.cyan( - ( 0, _path.join )( 'pages', previousPages[pageKey] ) ) - } and ${_chalk.default.cyan( - ( 0, _path.join )( 'pages', pagePath ) ) - } both resolve to ${_chalk.default.cyan( pageKey )}.` ); - - } else { - previousPages[pageKey] = pagePath; - } - result[pageKey] = ( 0, _path.join )( _constants.PAGES_DIR_ALIAS, pagePath ).replace( /\\/g, '/' ); - return result; - }, {} ); - - - pages['/_app'] = pages['/_app'] || 'next/dist/pages/_app'; - pages['/_error'] = pages['/_error'] || 'next/dist/pages/_error'; - pages['/_document'] = pages['/_document'] || 'next/dist/pages/_document'; - - Object.keys( pages ).forEach( key => { - if ( key.includes( '.' ) ) delete pages[key] - } ) - - return pages; -} - - - - -function createEntrypoints( - pages, - target, - buildId, - previewMode, - config ) { - const client = {}; - const server = {}; - - const hasRuntimeConfig = - Object.keys( config.publicRuntimeConfig ).length > 0 || - Object.keys( config.serverRuntimeConfig ).length > 0; - - const defaultServerlessOptions = { - absoluteAppPath: pages['/_app'], - absoluteDocumentPath: pages['/_document'], - absoluteErrorPath: pages['/_error'], - distDir: _constants.DOT_NEXT_ALIAS, - buildId, - assetPrefix: config.assetPrefix, - generateEtags: config.generateEtags, - canonicalBase: config.canonicalBase, - basePath: config.experimental.basePath, - runtimeConfig: hasRuntimeConfig ? - JSON.stringify( { - publicRuntimeConfig: config.publicRuntimeConfig, - serverRuntimeConfig: config.serverRuntimeConfig - } ) : - - '', - previewProps: JSON.stringify( previewMode ) - }; - - - Object.keys( pages ).forEach( page => { - const absolutePagePath = pages[page]; - const bundleFile = `${( 0, _normalizePagePath.normalizePagePath )( page )}.js`; - const isApiRoute = page.match( _constants.API_ROUTE ); - - const bundlePath = ( 0, _path.join )( 'static', buildId, 'pages', bundleFile ); - - const isLikeServerless = ( 0, _config.isTargetLikeServerless )( target ); - - if ( isApiRoute && isLikeServerless ) { - const serverlessLoaderOptions = { - page, - absolutePagePath, - ...defaultServerlessOptions - }; - - server[( 0, _path.join )( 'pages', bundleFile )] = `next-serverless-loader?${( 0, _querystring.stringify )( - serverlessLoaderOptions ) - }!`; - } else if ( isApiRoute || target === 'server' ) { - server[bundlePath] = [absolutePagePath]; - } else if ( isLikeServerless && page !== '/_app' && page !== '/_document' ) { - const serverlessLoaderOptions = { - page, - absolutePagePath, - ...defaultServerlessOptions - }; - - server[( 0, _path.join )( 'pages', bundleFile )] = `next-serverless-loader?${( 0, _querystring.stringify )( - serverlessLoaderOptions ) - }!`; - } - - if ( page === '/_document' ) { - return; - } - - if ( !isApiRoute ) { - const pageLoader = `next-client-pages-loader?${( 0, _querystring.stringify )( { - page, - absolutePagePath - } ) - }!`; - - // Make sure next/router is a dependency of _app or else granularChunks - // might cause the router to not be able to load causing hydration - // to fail - - client[bundlePath] = - page === '/_app' ? [pageLoader, require.resolve( '../client/router' )] : - pageLoader; - } - } ); - - return { - client, - server - }; - -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/index.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/index.js deleted file mode 100644 index 9bda45fd5e..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/index.js +++ /dev/null @@ -1,1248 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.default = build; -var _chalk = _interopRequireDefault( require( "chalk" ) ); -var _ciInfo = _interopRequireDefault( require( "ci-info" ) ); -var _crypto = _interopRequireDefault( require( "crypto" ) ); -var _devalue = _interopRequireDefault( require( "devalue" ) ); -var _escapeStringRegexp = _interopRequireDefault( require( "escape-string-regexp" ) ); -var _findUp = _interopRequireDefault( require( "find-up" ) ); -var _fs = _interopRequireDefault( require( "fs" ) ); -var _jestWorker = _interopRequireDefault( require( "jest-worker" ) ); -var _mkdirp = _interopRequireDefault( require( "mkdirp" ) ); -var _index = _interopRequireDefault( require( "next/dist/compiled/nanoid/index.js" ) ); -var _path = _interopRequireDefault( require( "path" ) ); -var _pathToRegexp = require( "path-to-regexp" ); -var _util = require( "util" ); -var _formatWebpackMessages = _interopRequireDefault( require( "../client/dev/error-overlay/format-webpack-messages" ) ); -var _checkCustomRoutes = _interopRequireWildcard( require( "../lib/check-custom-routes" ) ); - - - - -var _constants = require( "../lib/constants" ); - - - -var _findPagesDir = require( "../lib/find-pages-dir" ); -var _recursiveDelete = require( "../lib/recursive-delete" ); -var _recursiveReaddir = require( "../lib/recursive-readdir" ); -var _verifyTypeScriptSetup = require( "../lib/verifyTypeScriptSetup" ); -var _constants2 = require( "../next-server/lib/constants" ); - - - - -var _utils = require( "../next-server/lib/router/utils" ); - - - - -var _config = _interopRequireWildcard( require( "../next-server/server/config" ) ); - - -var _normalizePagePath = require( "../next-server/server/normalize-page-path" ); -var _events = require( "../telemetry/events" ); - - - - -var _storage = require( "../telemetry/storage" ); -var _compiler = require( "./compiler" ); -var _entries = require( "./entries" ); -var _generateBuildId = require( "./generate-build-id" ); -var _isWriteable = require( "./is-writeable" ); -var _spinner = _interopRequireDefault( require( "./spinner" ) ); -var _utils2 = require( "./utils" ); - - - - -var _webpackConfig = _interopRequireDefault( require( "./webpack-config" ) ); -var _writeBuildId = require( "./write-build-id" ); - -function _getRequireWildcardCache() { - if ( typeof WeakMap !== "function" ) return null; - var cache = new WeakMap(); - _getRequireWildcardCache = function () { - return cache; - }; - return cache; -} - -function _interopRequireWildcard( obj ) { - if ( obj && obj.__esModule ) { - return obj; - } - if ( obj === null || typeof obj !== "object" && typeof obj !== "function" ) { - return { - default: obj - }; - } - var cache = _getRequireWildcardCache(); - if ( cache && cache.has( obj ) ) { - return cache.get( obj ); - } - var newObj = {}; - var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; - for ( var key in obj ) { - if ( Object.prototype.hasOwnProperty.call( obj, key ) ) { - var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor( obj, key ) : null; - if ( desc && ( desc.get || desc.set ) ) { - Object.defineProperty( newObj, key, desc ); - } else { - newObj[key] = obj[key]; - } - } - } - newObj.default = obj; - if ( cache ) { - cache.set( obj, newObj ); - } - return newObj; -} - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} - -function _wrapRegExp( re, groups ) { - _wrapRegExp = function ( re, groups ) { - return new BabelRegExp( re, undefined, groups ); - }; - var _RegExp = _wrapNativeSuper( RegExp ); - var _super = RegExp.prototype; - var _groups = new WeakMap(); - - function BabelRegExp( re, flags, groups ) { - var _this = _RegExp.call( this, re, flags ); - _groups.set( _this, groups || _groups.get( re ) ); - return _this; - } - _inherits( BabelRegExp, _RegExp ); - BabelRegExp.prototype.exec = function ( str ) { - var result = _super.exec.call( this, str ); - if ( result ) result.groups = buildGroups( result, this ); - return result; - }; - BabelRegExp.prototype[Symbol.replace] = function ( str, substitution ) { - if ( typeof substitution === "string" ) { - var groups = _groups.get( this ); - return _super[Symbol.replace].call( this, str, substitution.replace( /\$<([^>]+)>/g, function ( _, name ) { - return "$" + groups[name]; - } ) ); - } else if ( typeof substitution === "function" ) { - var _this = this; - return _super[Symbol.replace].call( this, str, function () { - var args = []; - args.push.apply( args, arguments ); - if ( typeof args[args.length - 1] !== "object" ) { - args.push( buildGroups( args, _this ) ); - } - return substitution.apply( this, args ); - } ); - } else { - return _super[Symbol.replace].call( this, str, substitution ); - } - }; - - function buildGroups( result, re ) { - var g = _groups.get( re ); - return Object.keys( g ).reduce( function ( groups, name ) { - groups[name] = result[g[name]]; - return groups; - }, Object.create( null ) ); - } - return _wrapRegExp.apply( this, arguments ); -} - -function _inherits( subClass, superClass ) { - if ( typeof superClass !== "function" && superClass !== null ) { - throw new TypeError( "Super expression must either be null or a function" ); - } - subClass.prototype = Object.create( superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - } ); - if ( superClass ) _setPrototypeOf( subClass, superClass ); -} - -function _possibleConstructorReturn( self, call ) { - if ( call && ( typeof call === "object" || typeof call === "function" ) ) { - return call; - } - return _assertThisInitialized( self ); -} - -function _assertThisInitialized( self ) { - if ( self === void 0 ) { - throw new ReferenceError( "this hasn't been initialised - super() hasn't been called" ); - } - return self; -} - -function _wrapNativeSuper( Class ) { - var _cache = typeof Map === "function" ? new Map() : undefined; - _wrapNativeSuper = function _wrapNativeSuper( Class ) { - if ( Class === null || !_isNativeFunction( Class ) ) return Class; - if ( typeof Class !== "function" ) { - throw new TypeError( "Super expression must either be null or a function" ); - } - if ( typeof _cache !== "undefined" ) { - if ( _cache.has( Class ) ) return _cache.get( Class ); - _cache.set( Class, Wrapper ); - } - - function Wrapper() { - return _construct( Class, arguments, _getPrototypeOf( this ).constructor ); - } - Wrapper.prototype = Object.create( Class.prototype, { - constructor: { - value: Wrapper, - enumerable: false, - writable: true, - configurable: true - } - } ); - return _setPrototypeOf( Wrapper, Class ); - }; - return _wrapNativeSuper( Class ); -} - -function isNativeReflectConstruct() { - if ( typeof Reflect === "undefined" || !Reflect.construct ) return false; - if ( Reflect.construct.sham ) return false; - if ( typeof Proxy === "function" ) return true; - try { - Date.prototype.toString.call( Reflect.construct( Date, [], function () { } ) ); - return true; - } catch ( e ) { - return false; - } -} - -function _construct( Parent, args, Class ) { - if ( isNativeReflectConstruct() ) { - _construct = Reflect.construct; - } else { - _construct = function _construct( Parent, args, Class ) { - var a = [null]; - a.push.apply( a, args ); - var Constructor = Function.bind.apply( Parent, a ); - var instance = new Constructor(); - if ( Class ) _setPrototypeOf( instance, Class.prototype ); - return instance; - }; - } - return _construct.apply( null, arguments ); -} - -function _isNativeFunction( fn ) { - return Function.toString.call( fn ).indexOf( "[native code]" ) !== -1; -} - -function _setPrototypeOf( o, p ) { - _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf( o, p ) { - o.__proto__ = p; - return o; - }; - return _setPrototypeOf( o, p ); -} - -function _getPrototypeOf( o ) { - _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf( o ) { - return o.__proto__ || Object.getPrototypeOf( o ); - }; - return _getPrototypeOf( o ); -} - -const fsAccess = ( 0, _util.promisify )( _fs.default.access ); -const fsUnlink = ( 0, _util.promisify )( _fs.default.unlink ); -const fsRmdir = ( 0, _util.promisify )( _fs.default.rmdir ); -const fsStat = ( 0, _util.promisify )( _fs.default.stat ); -const fsMove = ( 0, _util.promisify )( _fs.default.rename ); -const fsReadFile = ( 0, _util.promisify )( _fs.default.readFile ); -const fsWriteFile = ( 0, _util.promisify )( _fs.default.writeFile ); -const mkdirp = ( 0, _util.promisify )( _mkdirp.default ); - -const staticCheckWorker = require.resolve( './utils' ); - - - - -async function build( dir, conf = null, destDir ) { - if ( !( await ( 0, _isWriteable.isWriteable )( dir ) ) ) { - throw new Error( - '> Build directory is not writeable. https://err.sh/zeit/next.js/build-dir-not-writeable' ); - - }; - - const config = ( 0, _config.default )( _constants2.PHASE_PRODUCTION_BUILD, dir, conf ); - const { - target - } = config; - const buildId = await ( 0, _generateBuildId.generateBuildId )( config.generateBuildId, _index.default ); - const distDir = _path.default.join( destDir, config.distDir ); - const headers = []; - const rewrites = []; - const redirects = []; - - if ( typeof config.experimental.redirects === 'function' ) { - const _redirects = await config.experimental.redirects(); - ( 0, _checkCustomRoutes.default )( _redirects, 'redirect' ); - redirects.push( ..._redirects ); - } - if ( typeof config.experimental.rewrites === 'function' ) { - const _rewrites = await config.experimental.rewrites(); - ( 0, _checkCustomRoutes.default )( _rewrites, 'rewrite' ); - rewrites.push( ..._rewrites ); - } - if ( typeof config.experimental.headers === 'function' ) { - const _headers = await config.experimental.headers(); - ( 0, _checkCustomRoutes.default )( _headers, 'header' ); - headers.push( ..._headers ); - } - - if ( _ciInfo.default.isCI ) { - const cacheDir = _path.default.join( distDir, 'cache' ); - const hasCache = await fsAccess( cacheDir ). - then( () => true ). - catch( () => false ); - - if ( !hasCache ) { - // Intentionally not piping to stderr in case people fail in CI when - // stderr is detected. - console.log( - _chalk.default.bold.yellow( `Warning: ` ) + - _chalk.default.bold( - `No build cache found. Please configure build caching for faster rebuilds. Read more: https://err.sh/next.js/no-cache` ) ); - - - console.log( '' ); - } - } - - const buildSpinner = ( 0, _spinner.default )( { - prefixText: 'Creating an optimized production build' - } ); - - - const telemetry = new _storage.Telemetry( { - distDir - } ); - - const publicDir = _path.default.join( dir, 'public' ); - const pagesDir = ( 0, _findPagesDir.findPagesDir )( dir ); - let publicFiles = []; - let hasPublicDir = false; - - telemetry.record( - ( 0, _events.eventCliSession )( _constants2.PHASE_PRODUCTION_BUILD, dir, { - cliCommand: 'build', - isSrcDir: _path.default.relative( dir, pagesDir ).startsWith( 'src' ), - hasNowJson: !!( await ( 0, _findUp.default )( 'now.json', { - cwd: dir - } ) ), - isCustomServer: null - } ) ); - - - - ( 0, _events.eventNextPlugins )( _path.default.resolve( dir ) ).then( events => telemetry.record( events ) ); - - await ( 0, _verifyTypeScriptSetup.verifyTypeScriptSetup )( dir, pagesDir ); - - try { - await fsStat( publicDir ); - hasPublicDir = true; - } catch ( _ ) { } - - if ( hasPublicDir ) { - publicFiles = await ( 0, _recursiveReaddir.recursiveReadDir )( publicDir, /.*/ ); - } - - let tracer = null; - if ( config.experimental.profiling ) { - const { - createTrace - } = require( './profiler/profiler.js' ); - tracer = createTrace( _path.default.join( distDir, `profile-events.json` ) ); - tracer.profiler.startProfiling(); - } - - const isLikeServerless = ( 0, _config.isTargetLikeServerless )( target ); - - const pagePaths = await ( 0, _utils2.collectPages )( - pagesDir, - config.pageExtensionsRnv || config.pageExtensions ); - - - // needed for static exporting since we want to replace with HTML - // files - const allStaticPages = new Set(); - let allPageInfos = new Map(); - - const previewProps = { - previewModeId: _crypto.default.randomBytes( 16 ).toString( 'hex' ), - previewModeSigningKey: _crypto.default.randomBytes( 32 ).toString( 'hex' ), - previewModeEncryptionKey: _crypto.default.randomBytes( 32 ).toString( 'hex' ) - }; - - - const mappedPages = ( 0, _entries.createPagesMapping )( pagePaths, config.pageExtensions ); - const entrypoints = ( 0, _entries.createEntrypoints )( - mappedPages, - target, - buildId, - previewProps, - config ); - const pageKeys = Object.keys( mappedPages ); - const dynamicRoutes = pageKeys.filter( page => ( 0, _utils.isDynamicRoute )( page ) ); - const conflictingPublicFiles = []; - const hasCustomErrorPage = mappedPages['/_error'].startsWith( - 'private-next-pages' ); - - const hasPages404 = Boolean( - mappedPages['/404'] && mappedPages['/404'].startsWith( 'private-next-pages' ) ); - - let hasNonStaticErrorPage; - - if ( hasPublicDir ) { - try { - await fsStat( _path.default.join( publicDir, '_next' ) ); - throw new Error( _constants.PUBLIC_DIR_MIDDLEWARE_CONFLICT ); - } catch ( err ) { } - } - - for ( let file of publicFiles ) { - file = file. - replace( /\\/g, '/' ). - replace( /\/index$/, '' ). - split( publicDir ). - pop(); - - if ( mappedPages[file] ) { - conflictingPublicFiles.push( file ); - } - } - const numConflicting = conflictingPublicFiles.length; - - if ( numConflicting ) { - throw new Error( - `Conflicting public and page file${ - numConflicting === 1 ? ' was' : 's were' - } found. https://err.sh/zeit/next.js/conflicting-public-file-page\n${conflictingPublicFiles.join( - '\n' ) - }` ); - - } - - const buildCustomRoute = ( - r, - - - - type ) => { - const keys = []; - const routeRegex = ( 0, _pathToRegexp.pathToRegexp )( r.source, keys, { - strict: true, - sensitive: false, - delimiter: '/' // default is `/#?`, but Next does not pass query info - } ); - - return { - ...r, - ...( type === 'redirect' ? { - statusCode: ( 0, _checkCustomRoutes.getRedirectStatus )( r ), - permanent: undefined - } : - - {} ), - regex: routeRegex.source - }; - - }; - - const routesManifestPath = _path.default.join( distDir, _constants2.ROUTES_MANIFEST ); - const routesManifest = { - version: 1, - pages404: true, - basePath: config.experimental.basePath, - redirects: redirects.map( r => buildCustomRoute( r, 'redirect' ) ), - rewrites: rewrites.map( r => buildCustomRoute( r, 'rewrite' ) ), - headers: headers.map( r => buildCustomRoute( r, 'header' ) ), - dynamicRoutes: ( 0, _utils.getSortedRoutes )( dynamicRoutes ).map( page => ( { - page, - regex: ( 0, _utils.getRouteRegex )( page ).re.source - } ) ) - }; - - - - await mkdirp( distDir ); - // We need to write the manifest with rewrites before build - // so serverless can import the manifest - await fsWriteFile( routesManifestPath, JSON.stringify( routesManifest ), 'utf8' ); - - const configs = await Promise.all( [ - ( 0, _webpackConfig.default )( destDir || dir, { - tracer, - buildId, - isServer: false, - config, - target, - pagesDir, - entrypoints: entrypoints.client - } ), - - ( 0, _webpackConfig.default )( destDir || dir, { - tracer, - buildId, - isServer: true, - config, - target, - pagesDir, - entrypoints: entrypoints.server - } ) - ] ); - - - - const clientConfig = configs[0]; - - if ( - clientConfig.optimization && ( - clientConfig.optimization.minimize !== true || - clientConfig.optimization.minimizer && - clientConfig.optimization.minimizer.length === 0 ) ) { - console.warn( - _chalk.default.bold.yellow( `Warning: ` ) + - _chalk.default.bold( - `Production code optimization has been disabled in your project. Read more: https://err.sh/zeit/next.js/minification-disabled` ) ); - - - } - - const webpackBuildStart = process.hrtime(); - - let result = { - warnings: [], - errors: [] - }; - // TODO: why do we need this?? https://github.com/zeit/next.js/issues/8253 - if ( isLikeServerless ) { - const clientResult = await ( 0, _compiler.runCompiler )( clientConfig ); - // Fail build if clientResult contains errors - if ( clientResult.errors.length > 0 ) { - result = { - warnings: [...clientResult.warnings], - errors: [...clientResult.errors] - }; - - } else { - const serverResult = await ( 0, _compiler.runCompiler )( configs[1] ); - result = { - warnings: [...clientResult.warnings, ...serverResult.warnings], - errors: [...clientResult.errors, ...serverResult.errors] - }; - - } - } else { - result = await ( 0, _compiler.runCompiler )( configs ); - } - - const webpackBuildEnd = process.hrtime( webpackBuildStart ); - if ( buildSpinner ) { - buildSpinner.stopAndPersist(); - } - console.log(); - - result = ( 0, _formatWebpackMessages.default )( result ); - - if ( result.errors.length > 0 ) { - // Only keep the first error. Others are often indicative - // of the same problem, but confuse the reader with noise. - if ( result.errors.length > 1 ) { - result.errors.length = 1; - } - const error = result.errors.join( '\n\n' ); - - console.error( _chalk.default.red( 'Failed to compile.\n' ) ); - - if ( - error.indexOf( 'private-next-pages' ) > -1 && - error.indexOf( 'does not contain a default export' ) > -1 ) { - const page_name_regex = _wrapRegExp( /'private\x2Dnext\x2Dpages\/([\0-&\(-\uFFFF]*)'/, { - page_name: 1 - } ); - const parsed = page_name_regex.exec( error ); - const page_name = parsed && parsed.groups && parsed.groups.page_name; - throw new Error( - `webpack build failed: found page without a React Component as default export in pages/${page_name}\n\nSee https://err.sh/zeit/next.js/page-without-valid-component for more info.` ); - - } - - console.error( error ); - console.error(); - - if ( - error.indexOf( 'private-next-pages' ) > -1 || - error.indexOf( '__next_polyfill__' ) > -1 ) { - throw new Error( - '> webpack config.resolve.alias was incorrectly overriden. https://err.sh/zeit/next.js/invalid-resolve-alias' ); - - } - throw new Error( '> Build failed because of webpack errors' ); - } else { - telemetry.record( - ( 0, _events.eventBuildCompleted )( pagePaths, { - durationInSeconds: webpackBuildEnd[0] - } ) ); - - - - if ( result.warnings.length > 0 ) { - console.warn( _chalk.default.yellow( 'Compiled with warnings.\n' ) ); - console.warn( result.warnings.join( '\n\n' ) ); - console.warn(); - } else { - console.log( _chalk.default.green( 'Compiled successfully.\n' ) ); - } - } - const postBuildSpinner = ( 0, _spinner.default )( { - prefixText: 'Automatically optimizing pages' - } ); - - - const manifestPath = _path.default.join( - distDir, - isLikeServerless ? _constants2.SERVERLESS_DIRECTORY : _constants2.SERVER_DIRECTORY, - _constants2.PAGES_MANIFEST ); - - const buildManifestPath = _path.default.join( distDir, _constants2.BUILD_MANIFEST ); - - const ssgPages = new Set(); - const ssgFallbackPages = new Set(); - const staticPages = new Set(); - const invalidPages = new Set(); - const hybridAmpPages = new Set(); - const serverPropsPages = new Set(); - const additionalSsgPaths = new Map(); - const pageInfos = new Map(); - const pagesManifest = JSON.parse( ( await fsReadFile( manifestPath, 'utf8' ) ) ); - const buildManifest = JSON.parse( ( await fsReadFile( buildManifestPath, 'utf8' ) ) ); - - let customAppGetInitialProps; - - process.env.NEXT_PHASE = _constants2.PHASE_PRODUCTION_BUILD; - - const staticCheckWorkers = new _jestWorker.default( staticCheckWorker, { - numWorkers: config.experimental.cpus, - enableWorkerThreads: config.experimental.workerThreads - } ); - - - staticCheckWorkers.getStdout().pipe( process.stdout ); - staticCheckWorkers.getStderr().pipe( process.stderr ); - - const runtimeEnvConfig = { - publicRuntimeConfig: config.publicRuntimeConfig, - serverRuntimeConfig: config.serverRuntimeConfig - }; - - - hasNonStaticErrorPage = - hasCustomErrorPage && ( - await ( 0, _utils2.hasCustomGetInitialProps )( - _path.default.join( - distDir, - ...( isLikeServerless ? ['serverless', 'pages'] : ['server', 'static', buildId, 'pages'] ), - '_error.js' ), - - runtimeEnvConfig ) ); - - - const analysisBegin = process.hrtime(); - await Promise.all( - pageKeys.map( async page => { - const actualPage = ( 0, _normalizePagePath.normalizePagePath )( page ); - const [selfSize, allSize] = await ( 0, _utils2.getPageSizeInKb )( - actualPage, - distDir, - buildId, - buildManifest, - config.experimental.modern ); - - const bundleRelative = _path.default.join( - isLikeServerless ? 'pages' : `static/${buildId}/pages`, - actualPage + '.js' ); - - const serverBundle = _path.default.join( - distDir, - isLikeServerless ? _constants2.SERVERLESS_DIRECTORY : _constants2.SERVER_DIRECTORY, - bundleRelative ); - - - let isSsg = false; - let isStatic = false; - let isHybridAmp = false; - let ssgPageRoutes = null; - let hasSsgFallback = false; - - pagesManifest[page] = bundleRelative.replace( /\\/g, '/' ); - - const nonReservedPage = !page.match( /^\/(_app|_error|_document|api)/ ); - - if ( nonReservedPage && customAppGetInitialProps === undefined ) { - customAppGetInitialProps = ( 0, _utils2.hasCustomGetInitialProps )( - isLikeServerless ? - serverBundle : - _path.default.join( - distDir, - _constants2.SERVER_DIRECTORY, - `/static/${buildId}/pages/_app.js` ), - - runtimeEnvConfig ); - - - if ( customAppGetInitialProps ) { - console.warn( - _chalk.default.bold.yellow( `Warning: ` ) + - _chalk.default.yellow( - `You have opted-out of Automatic Static Optimization due to \`getInitialProps\` in \`pages/_app\`.` ) ); - - - console.warn( - 'Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n' ); - - } - } - - if ( nonReservedPage ) { - try { - let result = await staticCheckWorkers.isPageStatic( - page, - serverBundle, - runtimeEnvConfig ); - - - if ( result.isHybridAmp ) { - isHybridAmp = true; - hybridAmpPages.add( page ); - } - - if ( result.hasStaticProps ) { - ssgPages.add( page ); - isSsg = true; - - if ( result.prerenderRoutes ) { - additionalSsgPaths.set( page, result.prerenderRoutes ); - ssgPageRoutes = result.prerenderRoutes; - } - if ( result.prerenderFallback ) { - hasSsgFallback = true; - ssgFallbackPages.add( page ); - } - } else if ( result.hasServerProps ) { - serverPropsPages.add( page ); - } else if ( result.isStatic && customAppGetInitialProps === false ) { - staticPages.add( page ); - isStatic = true; - } - - if ( hasPages404 && page === '/404' ) { - if ( !result.isStatic && !result.hasStaticProps ) { - throw new Error( _constants.PAGES_404_GET_INITIAL_PROPS_ERROR ); - } - // we need to ensure the 404 lambda is present since we use - // it when _app has getInitialProps - if ( customAppGetInitialProps && !result.hasStaticProps ) { - staticPages.delete( page ); - } - } - } catch ( err ) { - if ( err.message !== 'INVALID_DEFAULT_EXPORT' ) throw err; - invalidPages.add( page ); - } - } - - pageInfos.set( page, { - size: selfSize, - totalSize: allSize, - serverBundle, - static: isStatic, - isSsg, - isHybridAmp, - ssgPageRoutes, - hasSsgFallback - } ); - - } ) ); - - staticCheckWorkers.end(); - - if ( serverPropsPages.size > 0 || ssgPages.size > 0 ) { - // We update the routes manifest after the build with the - // data routes since we can't determine these until after build - routesManifest.dataRoutes = ( 0, _utils.getSortedRoutes )( [ - ...serverPropsPages, - ...ssgPages - ] ). - map( page => { - const pagePath = ( 0, _normalizePagePath.normalizePagePath )( page ); - const dataRoute = _path.default.posix.join( - '/_next/data', - buildId, - `${pagePath}.json` ); - - - return { - page, - dataRouteRegex: ( 0, _utils.isDynamicRoute )( page ) ? - ( 0, _utils.getRouteRegex )( dataRoute.replace( /\.json$/, '' ) ).re.source.replace( - /\(\?:\\\/\)\?\$$/, - '\\.json$' ) : - - new RegExp( - `^${_path.default.posix.join( - '/_next/data', - ( 0, _escapeStringRegexp.default )( buildId ), - `${pagePath}.json` ) - }$` ). - source - }; - - } ); - - await fsWriteFile( - routesManifestPath, - JSON.stringify( routesManifest ), - 'utf8' ); - - } - // Since custom _app.js can wrap the 404 page we have to opt-out of static optimization if it has getInitialProps - // Only export the static 404 when there is no /_error present - const useStatic404 = !customAppGetInitialProps && ( !hasNonStaticErrorPage || hasPages404 ); - - if ( invalidPages.size > 0 ) { - throw new Error( - `Build optimization failed: found page${ - invalidPages.size === 1 ? '' : 's' - } without a React Component as default export in \n${[...invalidPages]. - map( pg => `pages${pg}` ). - join( - '\n' ) - }\n\nSee https://err.sh/zeit/next.js/page-without-valid-component for more info.\n` ); - - } - - if ( Array.isArray( configs[0].plugins ) ) { - configs[0].plugins.some( plugin => { - if ( !plugin.ampPages ) { - return false; - } - - plugin.ampPages.forEach( pg => { - pageInfos.get( pg ).isAmp = true; - } ); - return true; - } ); - } - - await ( 0, _writeBuildId.writeBuildId )( distDir, buildId ); - - const finalPrerenderRoutes = {}; - const tbdPrerenderRoutes = []; - - if ( staticPages.size > 0 || ssgPages.size > 0 || useStatic404 ) { - const combinedPages = [...staticPages, ...ssgPages]; - const exportApp = require( '../export' ).default; - const exportOptions = { - silent: true, - buildExport: true, - threads: config.experimental.cpus, - pages: combinedPages, - outdir: _path.default.join( distDir, 'export' ) - }; - - const exportConfig = { - ...config, - initialPageRevalidationMap: {}, - // Default map will be the collection of automatic statically exported - // pages and SPR pages. - // n.b. we cannot handle this above in combinedPages because the dynamic - // page must be in the `pages` array, but not in the mapping. - exportPathMap: defaultMap => { - // Dynamically routed pages should be prerendered to be used as - // a client-side skeleton (fallback) while data is being fetched. - // This ensures the end-user never sees a 500 or slow response from the - // server. - // - // Note: prerendering disables automatic static optimization. - ssgPages.forEach( page => { - if ( ( 0, _utils.isDynamicRoute )( page ) ) { - tbdPrerenderRoutes.push( page ); - - if ( ssgFallbackPages.has( page ) ) { - // Override the rendering for the dynamic page to be treated as a - // fallback render. - defaultMap[page] = { - page, - query: { - __nextFallback: true - } - }; - } else { - // Remove dynamically routed pages from the default path map when - // fallback behavior is disabled. - delete defaultMap[page]; - } - } - } ); - // Append the "well-known" routes we should prerender for, e.g. blog - // post slugs. - additionalSsgPaths.forEach( ( routes, page ) => { - routes.forEach( route => { - defaultMap[route] = { - page - }; - } ); - } ); - - if ( useStatic404 ) { - defaultMap['/404'] = { - page: hasPages404 ? '/404' : '/_error' - }; - - } - - return defaultMap; - }, - exportTrailingSlash: false - }; - - - await exportApp( destDir || dir, exportOptions, exportConfig ); - - // remove server bundles that were exported - for ( const page of staticPages ) { - const { - serverBundle - } = pageInfos.get( page ); - await fsUnlink( serverBundle ); - } - - const moveExportedPage = async ( - page, - file, - isSsg, - ext ) => { - file = `${file}.${ext}`; - const orig = _path.default.join( exportOptions.outdir, file ); - const relativeDest = ( isLikeServerless ? - _path.default.join( 'pages', file ) : - _path.default.join( 'static', buildId, 'pages', file ) ). - replace( /\\/g, '/' ); - - const dest = _path.default.join( - distDir, - isLikeServerless ? _constants2.SERVERLESS_DIRECTORY : _constants2.SERVER_DIRECTORY, - relativeDest ); - - - if ( !isSsg ) { - pagesManifest[page] = relativeDest; - if ( page === '/' ) pagesManifest['/index'] = relativeDest; - if ( page === '/.amp' ) pagesManifest['/index.amp'] = relativeDest; - } - await mkdirp( _path.default.dirname( dest ) ); - await fsMove( orig, dest ); - }; - - // Only move /404 to /404 when there is no custom 404 as in that case we don't know about the 404 page - if ( !hasPages404 && useStatic404 ) { - await moveExportedPage( '/404', '/404', false, 'html' ); - } - - for ( const page of combinedPages ) { - const isSsg = ssgPages.has( page ); - const isSsgFallback = ssgFallbackPages.has( page ); - const isDynamic = ( 0, _utils.isDynamicRoute )( page ); - const hasAmp = hybridAmpPages.has( page ); - let file = ( 0, _normalizePagePath.normalizePagePath )( page ); - - // The dynamic version of SSG pages are only prerendered if the fallback - // is enabled. Below, we handle the specific prerenders of these. - if ( !( isSsg && isDynamic && !isSsgFallback ) ) { - await moveExportedPage( page, file, isSsg, 'html' ); - } - - if ( hasAmp ) { - await moveExportedPage( `${page}.amp`, `${file}.amp`, isSsg, 'html' ); - } - - if ( isSsg ) { - // For a non-dynamic SSG page, we must copy its data file from export. - if ( !isDynamic ) { - await moveExportedPage( page, file, true, 'json' ); - - finalPrerenderRoutes[page] = { - initialRevalidateSeconds: exportConfig.initialPageRevalidationMap[page], - srcRoute: null, - dataRoute: _path.default.posix.join( '/_next/data', buildId, `${file}.json` ) - }; - - } else { - // For a dynamic SSG page, we did not copy its data exports and only - // copy the fallback HTML file (if present). - // We must also copy specific versions of this page as defined by - // `getStaticPaths` (additionalSsgPaths). - const extraRoutes = additionalSsgPaths.get( page ) || []; - for ( const route of extraRoutes ) { - await moveExportedPage( route, route, true, 'html' ); - await moveExportedPage( route, route, true, 'json' ); - finalPrerenderRoutes[route] = { - initialRevalidateSeconds: exportConfig.initialPageRevalidationMap[route], - srcRoute: page, - dataRoute: _path.default.posix.join( - '/_next/data', - buildId, - `${( 0, _normalizePagePath.normalizePagePath )( route )}.json` ) - }; - - - } - } - } - } - - // remove temporary export folder - await ( 0, _recursiveDelete.recursiveDelete )( exportOptions.outdir ); - await fsRmdir( exportOptions.outdir ); - await fsWriteFile( manifestPath, JSON.stringify( pagesManifest ), 'utf8' ); - } - - if ( postBuildSpinner ) postBuildSpinner.stopAndPersist(); - console.log(); - - const analysisEnd = process.hrtime( analysisBegin ); - telemetry.record( - ( 0, _events.eventBuildOptimize )( pagePaths, { - durationInSeconds: analysisEnd[0], - staticPageCount: staticPages.size, - staticPropsPageCount: ssgPages.size, - serverPropsPageCount: serverPropsPages.size, - ssrPageCount: pagePaths.length - ( - staticPages.size + ssgPages.size + serverPropsPages.size ), - hasStatic404: useStatic404 - } ) ); - - - - if ( ssgPages.size > 0 ) { - const finalDynamicRoutes = {}; - tbdPrerenderRoutes.forEach( tbdRoute => { - const normalizedRoute = ( 0, _normalizePagePath.normalizePagePath )( tbdRoute ); - const dataRoute = _path.default.posix.join( - '/_next/data', - buildId, - `${normalizedRoute}.json` ); - - - finalDynamicRoutes[tbdRoute] = { - routeRegex: ( 0, _utils.getRouteRegex )( tbdRoute ).re.source, - dataRoute, - fallback: ssgFallbackPages.has( tbdRoute ) ? - `${normalizedRoute}.html` : false, - dataRouteRegex: ( 0, _utils.getRouteRegex )( - dataRoute.replace( /\.json$/, '' ) ). - re.source.replace( /\(\?:\\\/\)\?\$$/, '\\.json$' ) - }; - - } ); - const prerenderManifest = { - version: 2, - routes: finalPrerenderRoutes, - dynamicRoutes: finalDynamicRoutes, - preview: previewProps - }; - - - await fsWriteFile( - _path.default.join( distDir, _constants2.PRERENDER_MANIFEST ), - JSON.stringify( prerenderManifest ), - 'utf8' ); - - await generateClientSsgManifest( prerenderManifest, { - distDir, - buildId, - isModern: !!config.experimental.modern - } ); - - } else { - const prerenderManifest = { - version: 2, - routes: {}, - dynamicRoutes: {}, - preview: previewProps - }; - - await fsWriteFile( - _path.default.join( distDir, _constants2.PRERENDER_MANIFEST ), - JSON.stringify( prerenderManifest ), - 'utf8' ); - - // No need to call this fn as we already emitted a default SSG manifest: - // await generateClientSsgManifest(prerenderManifest, { distDir, buildId }) - } - - await fsWriteFile( - _path.default.join( distDir, _constants2.EXPORT_MARKER ), - JSON.stringify( { - version: 1, - hasExportPathMap: typeof config.exportPathMap === 'function', - exportTrailingSlash: config.exportTrailingSlash === true - } ), - - 'utf8' ); - - await fsUnlink( _path.default.join( distDir, _constants2.EXPORT_DETAIL ) ).catch( err => { - if ( err.code === 'ENOENT' ) { - return Promise.resolve(); - } - return Promise.reject( err ); - } ); - - staticPages.forEach( pg => allStaticPages.add( pg ) ); - pageInfos.forEach( ( info, key ) => { - allPageInfos.set( key, info ); - } ); - - await ( 0, _utils2.printTreeView )( - Object.keys( mappedPages ), - allPageInfos, - isLikeServerless, { - distPath: distDir, - buildId: buildId, - pagesDir, - useStatic404, - pageExtensions: config.pageExtensions, - buildManifest, - isModern: config.experimental.modern - } ); - - - ( 0, _utils2.printCustomRoutes )( { - redirects, - rewrites, - headers - } ); - - if ( tracer ) { - const parsedResults = await tracer.profiler.stopProfiling(); - await new Promise( resolve => { - if ( parsedResults === undefined ) { - tracer.profiler.destroy(); - tracer.trace.flush(); - tracer.end( resolve ); - return; - } - - const cpuStartTime = parsedResults.profile.startTime; - const cpuEndTime = parsedResults.profile.endTime; - - tracer.trace.completeEvent( { - name: 'TaskQueueManager::ProcessTaskFromWorkQueue', - id: ++tracer.counter, - cat: ['toplevel'], - ts: cpuStartTime, - args: { - src_file: '../../ipc/ipc_moji_bootstrap.cc', - src_func: 'Accept' - } - } ); - - - - tracer.trace.completeEvent( { - name: 'EvaluateScript', - id: ++tracer.counter, - cat: ['devtools.timeline'], - ts: cpuStartTime, - dur: cpuEndTime - cpuStartTime, - args: { - data: { - url: 'webpack', - lineNumber: 1, - columnNumber: 1, - frame: '0xFFF' - } - } - } ); - - - - - tracer.trace.instantEvent( { - name: 'CpuProfile', - id: ++tracer.counter, - cat: ['disabled-by-default-devtools.timeline'], - ts: cpuEndTime, - args: { - data: { - cpuProfile: parsedResults.profile - } - } - } ); - - - - - tracer.profiler.destroy(); - tracer.trace.flush(); - tracer.end( resolve ); - } ); - } - - await telemetry.flush(); -} - -function generateClientSsgManifest( - prerenderManifest, { - buildId, - distDir, - isModern - } ) { - const ssgPages = new Set( [ - ...Object.entries( prerenderManifest.routes ) - // Filter out dynamic routes - .filter( ( [, { - srcRoute - }] ) => srcRoute == null ). - map( ( [route] ) => route ), - ...Object.keys( prerenderManifest.dynamicRoutes ) - ] ); - - - const clientSsgManifestPaths = [ - '_ssgManifest.js', - isModern && '_ssgManifest.module.js' - ]. - - filter( Boolean ). - map( f => _path.default.join( `${_constants2.CLIENT_STATIC_FILES_PATH}/${buildId}`, f ) ); - const clientSsgManifestContent = `self.__SSG_MANIFEST=${( 0, _devalue.default )( - ssgPages ) - };self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()`; - clientSsgManifestPaths.forEach( ( clientSsgManifestPath ) => - _fs.default.writeFileSync( - _path.default.join( distDir, clientSsgManifestPath ), - clientSsgManifestContent ) ); - - -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/utils.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/utils.js deleted file mode 100644 index ee97eef713..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/utils.js +++ /dev/null @@ -1,748 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.collectPages = collectPages; -exports.printTreeView = printTreeView; -exports.printCustomRoutes = printCustomRoutes; -exports.getSharedSizes = getSharedSizes; -exports.getPageSizeInKb = getPageSizeInKb; -exports.buildStaticPaths = buildStaticPaths; -exports.isPageStatic = isPageStatic; -exports.hasCustomGetInitialProps = hasCustomGetInitialProps; -var _chalk = _interopRequireDefault(require("chalk")); -var _gzipSize = _interopRequireDefault(require("gzip-size")); -var _textTable = _interopRequireDefault(require("next/dist/compiled/text-table")); -var _path = _interopRequireDefault(require("path")); -var _reactIs = require("react-is"); -var _stripAnsi = _interopRequireDefault(require("strip-ansi")); - -var _constants = require("../lib/constants"); - - - - -var _prettyBytes = _interopRequireDefault(require("../lib/pretty-bytes")); -var _recursiveReaddir = require("../lib/recursive-readdir"); -var _utils = require("../next-server/lib/router/utils"); -var _isDynamic = require("../next-server/lib/router/utils/is-dynamic"); -var _findPageFile = require("../server/lib/find-page-file"); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} - - -const fileGzipStats = {}; -const fsStatGzip = file => { - if (fileGzipStats[file]) return fileGzipStats[file]; - fileGzipStats[file] = _gzipSize.default.file(file); - return fileGzipStats[file]; -}; - -function collectPages(directory, pageExtensions) { - return (0, _recursiveReaddir.recursiveReadDir)( - directory, - new RegExp(`\\.(?:${pageExtensions.join('|')})$`)); -} - -async function printTreeView( - list, - pageInfos, - serverless, { - distPath, - buildId, - pagesDir, - pageExtensions, - buildManifest, - isModern, - useStatic404 - }) -{ - const getPrettySize = _size => { - const size = (0, _prettyBytes.default)(_size); - // green for 0-130kb - if (_size < 130 * 1000) return _chalk.default.green(size); - // yellow for 130-170kb - if (_size < 170 * 1000) return _chalk.default.yellow(size); - // red for >= 170kb - return _chalk.default.red.bold(size); - }; - - const messages = [ - ['Page', 'Size', 'First Load'].map(entry => _chalk.default.underline(entry)) - ]; - - - - - const hasCustomApp = await (0, _findPageFile.findPageFile)(pagesDir, '/_app', pageExtensions); - const hasCustomError = await (0, _findPageFile.findPageFile)(pagesDir, '/_error', pageExtensions); - - if (useStatic404) { - pageInfos.set('/404', { - ...(pageInfos.get('/404') || pageInfos.get('/_error')), - static: true - }); - - list = [...list, '/404']; - } - - const pageList = list. - slice(). - filter( - (e) => - !( - e === '/_document' || - !hasCustomApp && e === '/_app' || - !hasCustomError && e === '/_error')). - - - sort((a, b) => a.localeCompare(b)); - - pageList.forEach((item, i, arr) => { - var _pageInfo$ssgPageRout; - const symbol = - i === 0 ? - arr.length === 1 ? - '─' : - '┌' : - i === arr.length - 1 ? - '└' : - '├'; - - const pageInfo = pageInfos.get(item); - - messages.push([ - `${symbol} ${ -item==='/_app'? -' ': -(pageInfo===null||pageInfo===void 0?void 0:pageInfo.static)? -'○': -(pageInfo===null||pageInfo===void 0?void 0:pageInfo.isSsg)? -'●': -'λ' -} ${item}`, - pageInfo ? - pageInfo.isAmp ? - _chalk.default.cyan('AMP') : - pageInfo.size >= 0 ? - (0, _prettyBytes.default)(pageInfo.size) : - '' : - '', - pageInfo ? - pageInfo.isAmp ? - _chalk.default.cyan('AMP') : - pageInfo.size >= 0 ? - getPrettySize(pageInfo.totalSize) : - '' : - '' - ]); - - - if (pageInfo === null || pageInfo === void 0 ? void 0 : (_pageInfo$ssgPageRout = pageInfo.ssgPageRoutes) === null || _pageInfo$ssgPageRout === void 0 ? void 0 : _pageInfo$ssgPageRout.length) { - const totalRoutes = pageInfo.ssgPageRoutes.length; - const previewPages = totalRoutes === 4 ? 4 : 3; - const contSymbol = i === arr.length - 1 ? ' ' : '├'; - - const routes = pageInfo.ssgPageRoutes.slice(0, previewPages); - if (totalRoutes > previewPages) { - const remaining = totalRoutes - previewPages; - routes.push(`[+${remaining} more paths]`); - } - - routes.forEach((slug, index, { - length - }) => { - const innerSymbol = index === length - 1 ? '└' : '├'; - messages.push([`${contSymbol} ${innerSymbol} ${slug}`, '', '']); - }); - } - }); - - const sharedData = await getSharedSizes( - distPath, - buildManifest, - buildId, - isModern, - pageInfos); - - - messages.push(['+ shared by all', getPrettySize(sharedData.total), '']); - Object.keys(sharedData.files). - map(e => e.replace(buildId, '')). - sort(). - forEach((fileName, index, { - length - }) => { - const innerSymbol = index === length - 1 ? '└' : '├'; - - const originalName = fileName.replace('', buildId); - const cleanName = fileName - // Trim off `static/` - .replace(/^static\//, '') - // Re-add `static/` for root files - .replace(/^/, 'static') - // Remove file hash - .replace(/[.-]([0-9a-z]{6})[0-9a-z]{14}(?=\.)/, '.$1'); - - messages.push([ - ` ${innerSymbol} ${cleanName}`, - (0, _prettyBytes.default)(sharedData.files[originalName]), - '' - ]); - - }); - - console.log( - (0, _textTable.default)(messages, { - align: ['l', 'l', 'r'], - stringLength: str => (0, _stripAnsi.default)(str).length - })); - - - - console.log(); - console.log( - (0, _textTable.default)( - [ - [ - 'λ', - serverless ? '(Lambda)' : '(Server)', - `server-side renders at runtime (uses ${_chalk.default.cyan( -'getInitialProps') -} or ${_chalk.default.cyan('getServerSideProps')})` - ], - - [ - '○', - '(Static)', - 'automatically rendered as static HTML (uses no initial props)' - ], - - [ - '●', - '(SSG)', - `automatically generated as static HTML + JSON (uses ${_chalk.default.cyan( -'getStaticProps') -})` - ] - ], - - - { - align: ['l', 'l', 'l'], - stringLength: str => (0, _stripAnsi.default)(str).length - })); - - - - - console.log(); -} - -function printCustomRoutes({ - redirects, - rewrites, - headers -}) - - - - -{ - const printRoutes = ( - routes, - type) => { - const isRedirects = type === 'Redirects'; - const isHeaders = type === 'Headers'; - console.log(_chalk.default.underline(type)); - console.log(); - - /* - ┌ source - ├ permanent/statusCode - └ destination - */ - const routesStr = routes. - map(route => { - let routeStr = `┌ source: ${route.source}\n`; - - if (!isHeaders) { - const r = route; - routeStr += `${isRedirects?'├':'└'} destination: ${ -r.destination -}\n`; - } - if (isRedirects) { - const r = route; - routeStr += `└ ${ -r.statusCode? -`status: ${r.statusCode}`: -`permanent: ${r.permanent}` -}\n`; - } - - if (isHeaders) { - const r = route; - routeStr += `└ headers:\n`; - - for (let i = 0; i < r.headers.length; i++) { - const header = r.headers[i]; - const last = i === headers.length - 1; - - routeStr += ` ${last?'└':'├'} ${header.key}: ${header.value}\n`; - } - } - - return routeStr; - }). - join('\n'); - - console.log(routesStr, '\n'); - }; - - if (redirects.length) { - printRoutes(redirects, 'Redirects'); - } - if (rewrites.length) { - printRoutes(rewrites, 'Rewrites'); - } - if (headers.length) { - printRoutes(headers, 'Headers'); - } -} - - - - -let cachedBuildManifest; - -let lastCompute; -let lastComputeModern; -let lastComputePageInfo; - -async function computeFromManifest( - manifest, - distPath, - buildId, - isModern, - pageInfos) { - if ( - Object.is(cachedBuildManifest, manifest) && - lastComputeModern === isModern && - lastComputePageInfo === !!pageInfos) { - return lastCompute; - } - - let expected = 0; - const files = new Map(); - Object.keys(manifest.pages).forEach(key => { - if (key === '/_polyfills') { - return; - } - - if (pageInfos) { - const cleanKey = key.replace(/\/index$/, '') || '/'; - const pageInfo = pageInfos.get(cleanKey); - // don't include AMP pages since they don't rely on shared bundles - if ((pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.isHybridAmp) || (pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.isAmp)) { - return; - } - } - - ++expected; - manifest.pages[key].forEach(file => { - if ( - // Filter out CSS - !file.endsWith('.js') || - // Select Modern or Legacy scripts - file.endsWith('.module.js') !== isModern) { - return; - } - - if (key === '/_app') { - files.set(file, Infinity); - } else if (files.has(file)) { - files.set(file, files.get(file) + 1); - } else { - files.set(file, 1); - } - }); - }); - - // Add well-known shared file - files.set( - _path.default.posix.join( - `static/${buildId}/pages/`, - `/_app${isModern?'.module':''}.js`), - - Infinity); - - - const commonFiles = [...files.entries()]. - filter(([, len]) => len === expected || len === Infinity). - map(([f]) => f); - const uniqueFiles = [...files.entries()]. - filter(([, len]) => len === 1). - map(([f]) => f); - - let stats; - try { - stats = await Promise.all( - commonFiles.map( - async (f) => [f, await fsStatGzip(_path.default.join(distPath, f))])); - - - } catch (_) { - stats = []; - } - - lastCompute = { - commonFiles, - uniqueFiles, - sizeCommonFile: stats.reduce( - (obj, n) => Object.assign(obj, { - [n[0]]: n[1] - }), {}), - - sizeCommonFiles: stats.reduce((size, [, stat]) => size + stat, 0) - }; - - - cachedBuildManifest = manifest; - lastComputeModern = isModern; - lastComputePageInfo = !!pageInfos; - return lastCompute; -} - -function difference(main, sub) { - const a = new Set(main); - const b = new Set(sub); - return [...a].filter(x => !b.has(x)); -} - -function intersect(main, sub) { - const a = new Set(main); - const b = new Set(sub); - return [...new Set([...a].filter(x => b.has(x)))]; -} - -function sum(a) { - return a.reduce((size, stat) => size + stat, 0); -} - -async function getSharedSizes( - distPath, - buildManifest, - buildId, - isModern, - pageInfos) { - const data = await computeFromManifest( - buildManifest, - distPath, - buildId, - isModern, - pageInfos); - - return { - total: data.sizeCommonFiles, - files: data.sizeCommonFile - }; -} - -async function getPageSizeInKb( - page, - distPath, - buildId, - buildManifest, - isModern) { - const data = await computeFromManifest( - buildManifest, - distPath, - buildId, - isModern); - - - const fnFilterModern = (entry) => - entry.endsWith('.js') && entry.endsWith('.module.js') === isModern; - - const pageFiles = (buildManifest.pages[page] || []).filter(fnFilterModern); - const appFiles = (buildManifest.pages['/_app'] || []).filter(fnFilterModern); - - const fnMapRealPath = dep => `${distPath}/${dep}`; - - const allFilesReal = [...new Set([...pageFiles, ...appFiles])].map( - fnMapRealPath); - - const selfFilesReal = difference( - intersect(pageFiles, data.uniqueFiles), - data.commonFiles). - map(fnMapRealPath); - - const clientBundle = _path.default.join( - distPath, - `static/${buildId}/pages/`, - `${page}${isModern?'.module':''}.js`); - - const appBundle = _path.default.join( - distPath, - `static/${buildId}/pages/`, - `/_app${isModern?'.module':''}.js`); - - selfFilesReal.push(clientBundle); - allFilesReal.push(clientBundle); - if (clientBundle !== appBundle) { - allFilesReal.push(appBundle); - } - - try { - // Doesn't use `Promise.all`, as we'd double compute duplicate files. This - // function is memoized, so the second one will instantly resolve. - const allFilesSize = sum((await Promise.all(allFilesReal.map(fsStatGzip)))); - const selfFilesSize = sum((await Promise.all(selfFilesReal.map(fsStatGzip)))); - return [selfFilesSize, allFilesSize]; - } catch (_) {} - return [-1, -1]; -} - -async function buildStaticPaths( - page, - getStaticPaths) { - const prerenderPaths = new Set(); - const _routeRegex = (0, _utils.getRouteRegex)(page); - const _routeMatcher = (0, _utils.getRouteMatcher)(_routeRegex); - - // Get the default list of allowed params. - const _validParamKeys = Object.keys(_routeMatcher(page)); - - const staticPathsResult = await getStaticPaths(); - - const expectedReturnVal = - `Expected: { paths: [], fallback: boolean }\n` + - `See here for more info: https://err.sh/zeit/next.js/invalid-getstaticpaths-value`; - - if ( - !staticPathsResult || - typeof staticPathsResult !== 'object' || - Array.isArray(staticPathsResult)) { - throw new Error( - `Invalid value returned from getStaticPaths in ${page}. Received ${typeof staticPathsResult} ${expectedReturnVal}`); - - } - - const invalidStaticPathKeys = Object.keys(staticPathsResult).filter( - key => !(key === 'paths' || key === 'fallback')); - - - if (invalidStaticPathKeys.length > 0) { - throw new Error( - `Extra keys returned from getStaticPaths in ${page} (${invalidStaticPathKeys.join( -', ') -}) ${expectedReturnVal}`); - - } - - if (typeof staticPathsResult.fallback !== 'boolean') { - throw new Error( - `The \`fallback\` key must be returned from getStaticPaths in ${page}.\n` + - expectedReturnVal); - - } - - const toPrerender = staticPathsResult.paths; - - if (!Array.isArray(toPrerender)) { - throw new Error( - `Invalid \`paths\` value returned from getStaticProps in ${page}.\n` + - `\`paths\` must be an array of strings or objects of shape { params: [key: string]: string }`); - - } - - toPrerender.forEach(entry => { - // For a string-provided path, we must make sure it matches the dynamic - // route. - if (typeof entry === 'string') { - const result = _routeMatcher(entry); - if (!result) { - throw new Error( - `The provided path \`${entry}\` does not match the page: \`${page}\`.`); - - } - - prerenderPaths === null || prerenderPaths === void 0 ? void 0 : prerenderPaths.add(entry); - } - // For the object-provided path, we must make sure it specifies all - // required keys. - else { - const invalidKeys = Object.keys(entry).filter(key => key !== 'params'); - if (invalidKeys.length) { - throw new Error( - `Additional keys were returned from \`getStaticPaths\` in page "${page}". ` + - `URL Parameters intended for this dynamic route must be nested under the \`params\` key, i.e.:` + - `\n\n\treturn { params: { ${_validParamKeys. -map(k=>`${k}: ...`). -join(', ')} } }` + - `\n\nKeys that need to be moved: ${invalidKeys.join(', ')}.\n`); - - } - - const { - params = {} - } = entry; - let builtPage = page; - _validParamKeys.forEach(validParamKey => { - const { - repeat - } = _routeRegex.groups[validParamKey]; - const paramValue = params[validParamKey]; - if ( - repeat && !Array.isArray(paramValue) || - !repeat && typeof paramValue !== 'string') { - throw new Error( - `A required parameter (${validParamKey}) was not provided as ${ -repeat?'an array':'a string' -} in getStaticPaths for ${page}`); - - } - - builtPage = builtPage.replace( - `[${repeat?'...':''}${validParamKey}]`, - repeat ? - paramValue.map(encodeURIComponent).join('/') : - encodeURIComponent(paramValue)); - - }); - - prerenderPaths === null || prerenderPaths === void 0 ? void 0 : prerenderPaths.add(builtPage); - } - }); - - return { - paths: [...prerenderPaths], - fallback: staticPathsResult.fallback - }; -} - -async function isPageStatic( - page, - serverBundle, - runtimeEnvConfig) - - - - -{ - try { - require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig); - const mod = require(serverBundle); - const Comp = mod.default || mod; - - if (!Comp || !(0, _reactIs.isValidElementType)(Comp) || typeof Comp === 'string') { - throw new Error('INVALID_DEFAULT_EXPORT'); - } - - const hasGetInitialProps = !!Comp.getInitialProps; - const hasStaticProps = !!mod.getStaticProps; - const hasStaticPaths = !!mod.getStaticPaths; - const hasServerProps = !!mod.getServerSideProps; - const hasLegacyServerProps = !!mod.unstable_getServerProps; - const hasLegacyStaticProps = !!mod.unstable_getStaticProps; - const hasLegacyStaticPaths = !!mod.unstable_getStaticPaths; - const hasLegacyStaticParams = !!mod.unstable_getStaticParams; - - if (hasLegacyStaticParams) { - throw new Error( - `unstable_getStaticParams was replaced with getStaticPaths. Please update your code.`); - - } - - if (hasLegacyStaticPaths) { - throw new Error( - `unstable_getStaticPaths was replaced with getStaticPaths. Please update your code.`); - - } - - if (hasLegacyStaticProps) { - throw new Error( - `unstable_getStaticProps was replaced with getStaticProps. Please update your code.`); - - } - - if (hasLegacyServerProps) { - throw new Error( - `unstable_getServerProps was replaced with getServerSideProps. Please update your code.`); - - } - - // A page cannot be prerendered _and_ define a data requirement. That's - // contradictory! - if (hasGetInitialProps && hasStaticProps) { - throw new Error(_constants.SSG_GET_INITIAL_PROPS_CONFLICT); - } - - if (hasGetInitialProps && hasServerProps) { - throw new Error(_constants.SERVER_PROPS_GET_INIT_PROPS_CONFLICT); - } - - if (hasStaticProps && hasServerProps) { - throw new Error(_constants.SERVER_PROPS_SSG_CONFLICT); - } - - const pageIsDynamic = (0, _isDynamic.isDynamicRoute)(page); - // A page cannot have static parameters if it is not a dynamic page. - if (hasStaticProps && hasStaticPaths && !pageIsDynamic) { - throw new Error( - `getStaticPaths can only be used with dynamic pages, not '${page}'.` + - `\nLearn more: https://nextjs.org/docs#dynamic-routing`); - - } - - if (hasStaticProps && pageIsDynamic && !hasStaticPaths) { - throw new Error( - `getStaticPaths is required for dynamic SSG pages and is missing for '${page}'.` + - `\nRead more: https://err.sh/next.js/invalid-getstaticpaths-value`); - - } - - let prerenderRoutes; - let prerenderFallback; - if (hasStaticProps && hasStaticPaths) { - ; - ({ - paths: prerenderRoutes, - fallback: prerenderFallback - } = - await buildStaticPaths(page, mod.getStaticPaths)); - } - - const config = mod.config || {}; - return { - isStatic: !hasStaticProps && !hasGetInitialProps && !hasServerProps, - isHybridAmp: config.amp === 'hybrid', - prerenderRoutes, - prerenderFallback, - hasStaticProps, - hasServerProps - }; - - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') return {}; - throw err; - } -} - -function hasCustomGetInitialProps( - bundle, - runtimeEnvConfig) { - require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig); - let mod = require(bundle); - - if (bundle.endsWith('_app.js') || bundle.endsWith('_error.js')) { - mod = mod.default || mod; - } else { - // since we don't output _app in serverless mode get it from a page - mod = mod._app; - } - return mod.getInitialProps !== mod.origGetInitialProps; -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/webpack-config.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/webpack-config.js deleted file mode 100644 index d2fc42525d..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/build/webpack-config.js +++ /dev/null @@ -1,832 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.default = getBaseWebpackConfig; -var _chalk = _interopRequireDefault( require( "chalk" ) ); -var _crypto = _interopRequireDefault( require( "crypto" ) ); -var _forkTsCheckerWebpackPlugin = _interopRequireDefault( require( "fork-ts-checker-webpack-plugin" ) ); -var _path = _interopRequireDefault( require( "path" ) ); -var _pnpWebpackPlugin = _interopRequireDefault( require( "pnp-webpack-plugin" ) ); -var _webpack = _interopRequireDefault( require( "webpack" ) ); -var _constants = require( "../lib/constants" ); -var _fileExists = require( "../lib/file-exists" ); -var _resolveRequest = require( "../lib/resolve-request" ); -var _constants2 = require( "../next-server/lib/constants" ); -var _findPageFile = require( "../server/lib/find-page-file" ); -var _collectPlugins = require( "./plugins/collect-plugins" ); -var _config = require( "./webpack/config" ); -var _overrideCssConfiguration = require( "./webpack/config/blocks/css/overrideCssConfiguration" ); -var _nextPluginLoader = require( "./webpack/loaders/next-plugin-loader" ); -var _buildManifestPlugin = _interopRequireDefault( require( "./webpack/plugins/build-manifest-plugin" ) ); -var _chunkNamesPlugin = _interopRequireDefault( require( "./webpack/plugins/chunk-names-plugin" ) ); -var _cssMinimizerPlugin = require( "./webpack/plugins/css-minimizer-plugin" ); -var _dllImport = require( "./webpack/plugins/dll-import" ); -var _nextDropClientPagePlugin = require( "./webpack/plugins/next-drop-client-page-plugin" ); -var _nextEsmPlugin = _interopRequireDefault( require( "./webpack/plugins/next-esm-plugin" ) ); -var _nextjsSsrImport = _interopRequireDefault( require( "./webpack/plugins/nextjs-ssr-import" ) ); -var _nextjsSsrModuleCache = _interopRequireDefault( require( "./webpack/plugins/nextjs-ssr-module-cache" ) ); -var _pagesManifestPlugin = _interopRequireDefault( require( "./webpack/plugins/pages-manifest-plugin" ) ); -var _profilingPlugin = require( "./webpack/plugins/profiling-plugin" ); -var _reactLoadablePlugin = require( "./webpack/plugins/react-loadable-plugin" ); -var _serverlessPlugin = require( "./webpack/plugins/serverless-plugin" ); -var _index = require( "./webpack/plugins/terser-webpack-plugin/src/index" ); -var _webpackConformancePlugin = _interopRequireWildcard( require( "./webpack/plugins/webpack-conformance-plugin" ) ); - -function _getRequireWildcardCache() { - if ( typeof WeakMap !== "function" ) return null; - var cache = new WeakMap(); - _getRequireWildcardCache = function () { - return cache; - }; - return cache; -} - -function _interopRequireWildcard( obj ) { - if ( obj && obj.__esModule ) { - return obj; - } - if ( obj === null || typeof obj !== "object" && typeof obj !== "function" ) { - return { - default: obj - }; - } - var cache = _getRequireWildcardCache(); - if ( cache && cache.has( obj ) ) { - return cache.get( obj ); - } - var newObj = {}; - var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; - for ( var key in obj ) { - if ( Object.prototype.hasOwnProperty.call( obj, key ) ) { - var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor( obj, key ) : null; - if ( desc && ( desc.get || desc.set ) ) { - Object.defineProperty( newObj, key, desc ); - } - else { - newObj[key] = obj[key]; - } - } - } - newObj.default = obj; - if ( cache ) { - cache.set( obj, newObj ); - } - return newObj; -} - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : - { - default: obj - }; -} -const escapePathVariables = value => { - return typeof value === 'string' ? value.replace( /\[(\\*[\w:]+\\*)\]/gi, '[\\$1\\]' ) : value; -}; - -function getOptimizedAliases( isServer ) { - if ( isServer ) { - return {}; - } - const stubWindowFetch = _path.default.join( __dirname, 'polyfills', 'fetch', 'index.js' ); - const stubObjectAssign = _path.default.join( __dirname, 'polyfills', 'object-assign.js' ); - const shimAssign = _path.default.join( __dirname, 'polyfills', 'object.assign' ); - return Object.assign( - {}, - { - unfetch$: stubWindowFetch, - 'isomorphic-unfetch$': stubWindowFetch, - 'whatwg-fetch$': _path.default.join( __dirname, 'polyfills', 'fetch', 'whatwg-fetch.js' ) - }, - { - 'object-assign$': stubObjectAssign, // Stub Package: object.assign - 'object.assign/auto': _path.default.join( shimAssign, 'auto.js' ), - 'object.assign/implementation': _path.default.join( shimAssign, 'implementation.js' ), - 'object.assign$': _path.default.join( shimAssign, 'index.js' ), - 'object.assign/polyfill': _path.default.join( shimAssign, 'polyfill.js' ), - 'object.assign/shim': _path.default.join( shimAssign, 'shim.js' ), // Replace: full URL polyfill with platform-based polyfill - url: require.resolve( 'native-url' ) - } ); -} -async function getBaseWebpackConfig( dir, - { - buildId, - config, - dev = false, - isServer = false, - pagesDir, - tracer, - target = 'server', - entrypoints - } ) { - var _config$typescript, _config$typescript2, _config$conformance, _config$conformance$D; - let plugins = []; - let babelPresetPlugins = []; - if ( config.experimental.plugins ) { - plugins = await ( 0, _collectPlugins.collectPlugins )( dir, config.env, config.plugins ); - _nextPluginLoader.pluginLoaderOptions.plugins = plugins; - for ( const plugin of plugins ) { - if ( plugin.middleware.includes( 'babel-preset-build' ) ) { - babelPresetPlugins.push( - { - dir: plugin.directory, - config: plugin.config - } ); - } - } - } - const distDir = _path.default.join( dir, config.distDir ); - const defaultLoaders = { - babel: - { - loader: 'next-babel-loader', - options: - { - isServer, - distDir, - pagesDir, - cwd: dir, - cache: true, - babelPresetPlugins, - hasModern: !!config.experimental.modern, - development: dev - } - }, // Backwards compat - hotSelfAccept: - { - loader: 'noop-loader' - } - }; - const babelIncludeRegexes = [/next[\\/]dist[\\/]next-server[\\/]lib/, /next[\\/]dist[\\/]client/, /next[\\/]dist[\\/]pages/, /[\\/](strip-ansi|ansi-regex)[\\/]/, ...( config.experimental.plugins ? _collectPlugins.VALID_MIDDLEWARE.map( name => new RegExp( `src(\\\\|/)${name}` ) ) : [] )]; // Support for NODE_PATH - const nodePathList = ( process.env.NODE_PATH || '' ).split( process.platform === 'win32' ? ';' : ':' ).filter( p => !!p ); - const isServerless = target === 'serverless'; - const isServerlessTrace = target === 'experimental-serverless-trace'; // Intentionally not using isTargetLikeServerless helper - const isLikeServerless = isServerless || isServerlessTrace; - const outputDir = isLikeServerless ? _constants2.SERVERLESS_DIRECTORY : _constants2.SERVER_DIRECTORY; - const outputPath = _path.default.join( distDir, isServer ? outputDir : '' ); - const totalPages = Object.keys( entrypoints ).length; - const clientEntries = !isServer ? - { // Backwards compatibility - 'main.js': [], - [_constants2.CLIENT_STATIC_FILES_RUNTIME_MAIN]: `.${_path.default.sep}` + _path.default.relative( dir, _path.default.join( _constants.NEXT_PROJECT_ROOT_DIST_CLIENT, dev ? `next-dev.js` : 'next.js' ) ), - [_constants2.CLIENT_STATIC_FILES_RUNTIME_POLYFILLS]: _path.default.join( _constants.NEXT_PROJECT_ROOT_DIST_CLIENT, 'polyfills.js' ) - } : undefined; - let typeScriptPath; - try { - typeScriptPath = ( 0, _resolveRequest.resolveRequest )( 'typescript', `${dir}/` ); - } - catch ( _ ) { } - const tsConfigPath = _path.default.join( dir, 'tsconfig.json' ); - const useTypeScript = Boolean( typeScriptPath && ( await ( 0, _fileExists.fileExists )( tsConfigPath ) ) ); - const ignoreTypeScriptErrors = dev ? ( _config$typescript = config.typescript ) === null || _config$typescript === void 0 ? void 0 : _config$typescript.ignoreDevErrors : ( _config$typescript2 = config.typescript ) === null || _config$typescript2 === void 0 ? void 0 : _config$typescript2.ignoreBuildErrors; - const rnvExtensions = config.pageExtensionsRnv; - const rnvExtensionsWitMjsSwapped = config.pageExtensionsRnv.filter( e => !e.includes( '.mjs' ) ); - rnvExtensionsWitMjsSwapped.splice( rnvExtensionsWitMjsSwapped.indexOf( '.js' ) + 1, 0, '.mjs' ); - const resolveConfig = { // Disable .mjs for node_modules bundling - extensions: isServer ? [...( useTypeScript ? ['.tsx', '.ts'] : [] ), '.js', '.mjs', '.jsx', '.json', '.wasm'] : [...( useTypeScript ? ['.tsx', '.ts'] : [] ), '.mjs', '.js', '.jsx', '.json', '.wasm'], - modules: ['node_modules', ...nodePathList // Support for NODE_PATH environment variable - ], - alias: - { // These aliases make sure the wrapper module is not included in the bundles - // Which makes bundles slightly smaller, but also skips parsing a module that we know will result in this alias - 'next/head': 'next/dist/next-server/lib/head.js', - 'next/router': 'next/dist/client/router.js', - 'next/config': 'next/dist/next-server/lib/runtime-config.js', - 'next/dynamic': 'next/dist/next-server/lib/dynamic.js', - next: _constants.NEXT_PROJECT_ROOT, - [_constants.PAGES_DIR_ALIAS]: pagesDir, - [_constants.DOT_NEXT_ALIAS]: distDir, - ...getOptimizedAliases( isServer ) - }, - mainFields: isServer ? ['main', 'module'] : ['browser', 'module', 'main'], - plugins: [_pnpWebpackPlugin.default] - }; - const webpackMode = dev ? 'development' : 'production'; - const terserPluginConfig = { - cache: true, - cpus: config.experimental.cpus, - distDir: distDir, - parallel: true, - sourceMap: false, - workerThreads: config.experimental.workerThreads - }; - const terserOptions = { - parse: - { - ecma: 8 - }, - compress: - { - ecma: 5, - warnings: false, // The following two options are known to break valid JavaScript code - comparisons: false, - inline: 2 // https://github.com/zeit/next.js/issues/7178#issuecomment-493048965 - }, - mangle: - { - safari10: true - }, - output: - { - ecma: 5, - safari10: true, - comments: false, // Fixes usage of Emoji and certain Regex - ascii_only: true - } - }; - const devtool = dev ? 'cheap-module-source-map' : false; - const isModuleCSS = module => { - return ( // mini-css-extract-plugin - module.type === `css/mini-extract` || // extract-css-chunks-webpack-plugin (old) - module.type === `css/extract-chunks` || // extract-css-chunks-webpack-plugin (new) - module.type === `css/extract-css-chunks` ); - }; // Contains various versions of the Webpack SplitChunksPlugin used in different build types - const splitChunksConfigs = { - dev: - { - cacheGroups: - { - default: false, - vendors: false - } - }, - prod: - { - chunks: 'all', - cacheGroups: - { - default: false, - vendors: false, - commons: - { - name: 'commons', - chunks: 'all', - minChunks: totalPages > 2 ? totalPages * 0.5 : 2 - }, - react: - { - name: 'commons', - chunks: 'all', - test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/ - } - } - }, - prodGranular: - { - chunks: 'all', - cacheGroups: - { - default: false, - vendors: false, - framework: - { - chunks: 'all', - name: 'framework', // This regex ignores nested copies of framework libraries so they're - // bundled with their issuer. - // https://github.com/zeit/next.js/pull/9012 - test: /(? 160000 && /node_modules[/\\]/.test( module.identifier() ); - }, - name( module ) { - const hash = _crypto.default.createHash( 'sha1' ); - if ( isModuleCSS( module ) ) { - module.updateHash( hash ); - } - else { - if ( !module.libIdent ) { - throw new Error( `Encountered unknown module type: ${module.type}. Please open an issue.` ); - } - hash.update( module.libIdent( - { - context: dir - } ) ); - } - return hash.digest( 'hex' ).substring( 0, 8 ); - }, - priority: 30, - minChunks: 1, - reuseExistingChunk: true - }, - commons: - { - name: 'commons', - minChunks: totalPages, - priority: 20 - }, - shared: - { - name( module, chunks ) { - return _crypto.default.createHash( 'sha1' ).update( chunks.reduce( ( acc, chunk ) => { - return acc + chunk.name; - }, '' ) ).digest( 'hex' ) + ( isModuleCSS( module ) ? '_CSS' : '' ); - }, - priority: 10, - minChunks: 2, - reuseExistingChunk: true - } - }, - maxInitialRequests: 25, - minSize: 20000 - } - }; // Select appropriate SplitChunksPlugin config for this build - let splitChunksConfig; - if ( dev ) { - splitChunksConfig = splitChunksConfigs.dev; - } - else { - splitChunksConfig = config.experimental.granularChunks ? splitChunksConfigs.prodGranular : splitChunksConfigs.prod; - } - const crossOrigin = !config.crossOrigin && config.experimental.modern ? 'anonymous' : config.crossOrigin; - let customAppFile = config.experimental.css ? await ( 0, _findPageFile.findPageFile )( pagesDir, '/_app', config.pageExtensions ) : null; - if ( customAppFile ) { - customAppFile = _path.default.resolve( _path.default.join( pagesDir, customAppFile ) ); - } - const conformanceConfig = Object.assign( - { - ReactSyncScriptsConformanceCheck: - { - enabled: true - }, - MinificationConformanceCheck: - { - enabled: true - }, - DuplicatePolyfillsConformanceCheck: - { - enabled: true, - BlockedAPIToBePolyfilled: Object.assign( [], ['fetch'], ( ( _config$conformance = config.conformance ) === null || _config$conformance === void 0 ? void 0 : ( _config$conformance$D = _config$conformance.DuplicatePolyfillsConformanceCheck ) === null || _config$conformance$D === void 0 ? void 0 : _config$conformance$D.BlockedAPIToBePolyfilled ) || [] ) - } - }, config.conformance ); - let webpackConfig = { - externals: !isServer ? undefined : !isServerless ? [( context, request, callback ) => { - const notExternalModules = ['next/app', 'next/document', 'next/link', 'next/error', 'string-hash', 'next/constants']; - if ( notExternalModules.indexOf( request ) !== -1 ) { - return callback(); - } // make sure we don't externalize anything that is - // supposed to be transpiled - if ( babelIncludeRegexes.some( r => r.test( request ) ) ) { - return callback(); - } // Relative requires don't need custom resolution, because they - // are relative to requests we've already resolved here. - // Absolute requires (require('/foo')) are extremely uncommon, but - // also have no need for customization as they're already resolved. - if ( request.startsWith( '.' ) || request.startsWith( '/' ) ) { - return callback(); - } // Resolve the import with the webpack provided context, this - // ensures we're resolving the correct version when multiple - // exist. - let res; - try { - res = ( 0, _resolveRequest.resolveRequest )( request, `${context}/` ); - } - catch ( err ) { // If the request cannot be resolved, we need to tell webpack to - // "bundle" it so that webpack shows an error (that it cannot be - // resolved). - return callback(); - } // Same as above, if the request cannot be resolved we need to have - // webpack "bundle" it so it surfaces the not found error. - if ( !res ) { - return callback(); - } // Bundled Node.js code is relocated without its node_modules tree. - // This means we need to make sure its request resolves to the same - // package that'll be available at runtime. If it's not identical, - // we need to bundle the code (even if it _should_ be external). - let baseRes; - try { - baseRes = ( 0, _resolveRequest.resolveRequest )( request, `${dir}/` ); - } - catch ( err ) { } // Same as above: if the package, when required from the root, - // would be different from what the real resolution would use, we - // cannot externalize it. - if ( baseRes !== res ) { - return callback(); - } // Default pages have to be transpiled - if ( !res.match( /next[/\\]dist[/\\]next-server[/\\]/ ) && ( res.match( /[/\\]next[/\\]dist[/\\]/ ) || // This is the @babel/plugin-transform-runtime "helpers: true" option - res.match( /node_modules[/\\]@babel[/\\]runtime[/\\]/ ) ) ) { - return callback(); - } // Webpack itself has to be compiled because it doesn't always use module relative paths - if ( res.match( /node_modules[/\\]webpack/ ) || res.match( /node_modules[/\\]css-loader/ ) ) { - return callback(); - } // Anything else that is standard JavaScript within `node_modules` - // can be externalized. - if ( res.match( /node_modules[/\\].*\.js$/ ) ) { - return callback( undefined, `commonjs ${request}` ); - } // Default behavior: bundle the code! - callback(); - }] : [ // When the 'serverless' target is used all node_modules will be compiled into the output bundles - // So that the 'serverless' bundles have 0 runtime dependencies - '@ampproject/toolbox-optimizer' // except this one - ], - optimization: - { - checkWasmTypes: false, - nodeEnv: false, - splitChunks: isServer ? false : splitChunksConfig, - runtimeChunk: isServer ? undefined : - { - name: _constants2.CLIENT_STATIC_FILES_RUNTIME_WEBPACK - }, - minimize: !( dev || isServer ), - minimizer: [ // Minify JavaScript - new _index.TerserPlugin( - { - ...terserPluginConfig, - terserOptions - } ), // Minify CSS - config.experimental.css && new _cssMinimizerPlugin.CssMinimizerPlugin( - { - postcssOptions: - { - map: - { // `inline: false` generates the source map in a separate file. - // Otherwise, the CSS file is needlessly large. - inline: false, // `annotation: false` skips appending the `sourceMappingURL` - // to the end of the CSS file. Webpack already handles this. - annotation: false - } - } - } ) - ].filter( Boolean ) - }, - context: dir, - node: - { - setImmediate: false - }, // Kept as function to be backwards compatible - entry: async () => { - return { - ...( clientEntries ? clientEntries : - {} ), - ...entrypoints, - ...( isServer ? - { - 'init-server.js': 'next-plugin-loader?middleware=on-init-server!', - 'on-error-server.js': 'next-plugin-loader?middleware=on-error-server!' - } : - {} ) - }; - }, - output: - { - path: outputPath, - filename: ( - { - chunk - } ) => { // Use `[name]-[contenthash].js` in production - if ( !dev && ( chunk.name === _constants2.CLIENT_STATIC_FILES_RUNTIME_MAIN || chunk.name === _constants2.CLIENT_STATIC_FILES_RUNTIME_WEBPACK || chunk.name === _constants2.CLIENT_STATIC_FILES_RUNTIME_POLYFILLS ) ) { - return chunk.name.replace( /\.js$/, '-[contenthash].js' ); - } - return '[name]'; - }, - libraryTarget: isServer ? 'commonjs2' : 'var', - hotUpdateChunkFilename: 'static/webpack/[id].[hash].hot-update.js', - hotUpdateMainFilename: 'static/webpack/[hash].hot-update.json', // This saves chunks with the name given via `import()` - chunkFilename: isServer ? `${dev ? '[name]' : '[name].[contenthash]'}.js` : `static/chunks/${dev ? '[name]' : '[name].[contenthash]'}.js`, - strictModuleExceptionHandling: true, - crossOriginLoading: crossOrigin, - futureEmitAssets: !dev, - webassemblyModuleFilename: 'static/wasm/[modulehash].wasm' - }, - performance: false, - resolve: resolveConfig, - resolveLoader: - { // The loaders Next.js provides - alias: ['emit-file-loader', 'error-loader', 'next-babel-loader', 'next-client-pages-loader', 'next-data-loader', 'next-serverless-loader', 'noop-loader', 'next-plugin-loader'].reduce( ( alias, loader ) => { // using multiple aliases to replace `resolveLoader.modules` - alias[loader] = _path.default.join( __dirname, 'webpack', 'loaders', loader ); - return alias; - }, - {} ), - modules: ['node_modules', ...nodePathList // Support for NODE_PATH environment variable - ], - plugins: [_pnpWebpackPlugin.default] - }, - module: - { - rules: [ - { - test: /\.(tsx|ts|js|mjs|jsx)$/, - include: [dir, ...babelIncludeRegexes], - exclude: path => { - if ( babelIncludeRegexes.some( r => r.test( path ) ) ) { - return false; - } - return /node_modules/.test( path ); - }, - use: config.experimental.babelMultiThread ? [ // Move Babel transpilation into a thread pool (2 workers, unlimited batch size). - // Applying a cache to the off-thread work avoids paying transfer costs for unchanged modules. - { - loader: 'cache-loader', - options: - { - cacheContext: dir, - cacheDirectory: _path.default.join( dir, '.next', 'cache', 'webpack' ), - cacheIdentifier: `webpack${isServer ? '-server' : ''}${config.experimental.modern ? '-hasmodern' : ''}` - } - }, - { - loader: 'thread-loader', - options: - { - workers: 2, - workerParallelJobs: Infinity - } - }, - defaultLoaders.babel - ] : defaultLoaders.babel - }].filter( Boolean ) - }, - plugins: [ // This plugin makes sure `output.filename` is used for entry chunks - new _chunkNamesPlugin.default(), new _webpack.default.DefinePlugin( - { - ...Object.keys( config.env ).reduce( ( acc, key ) => { - if ( /^(?:NODE_.+)|^(?:__.+)$/i.test( key ) ) { - throw new Error( `The key "${key}" under "env" in next.config.js is not allowed. https://err.sh/zeit/next.js/env-key-not-allowed` ); - } - return { - ...acc, - [`process.env.${key}`]: JSON.stringify( config.env[key] ) - }; - }, - {} ), - 'process.env.NODE_ENV': JSON.stringify( webpackMode ), - 'process.crossOrigin': JSON.stringify( crossOrigin ), - 'process.browser': JSON.stringify( !isServer ), - 'process.env.__NEXT_TEST_MODE': JSON.stringify( process.env.__NEXT_TEST_MODE ), // This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory - ...( dev && !isServer ? - { - 'process.env.__NEXT_DIST_DIR': JSON.stringify( distDir ) - } : - {} ), - 'process.env.__NEXT_EXPORT_TRAILING_SLASH': JSON.stringify( config.exportTrailingSlash ), - 'process.env.__NEXT_MODERN_BUILD': JSON.stringify( config.experimental.modern && !dev ), - 'process.env.__NEXT_GRANULAR_CHUNKS': JSON.stringify( config.experimental.granularChunks && !dev ), - 'process.env.__NEXT_BUILD_INDICATOR': JSON.stringify( config.devIndicators.buildActivity ), - 'process.env.__NEXT_PRERENDER_INDICATOR': JSON.stringify( config.devIndicators.autoPrerender ), - 'process.env.__NEXT_PLUGINS': JSON.stringify( config.experimental.plugins ), - 'process.env.__NEXT_STRICT_MODE': JSON.stringify( config.reactStrictMode ), - 'process.env.__NEXT_REACT_MODE': JSON.stringify( config.experimental.reactMode ), - 'process.env.__NEXT_ROUTER_BASEPATH': JSON.stringify( config.experimental.basePath ), - ...( isServer ? - { // Fix bad-actors in the npm ecosystem (e.g. `node-formidable`) - // This is typically found in unmaintained modules from the - // pre-webpack era (common in server-side code) - 'global.GENTLY': JSON.stringify( false ) - } : undefined ) - } ), !isServer && new _reactLoadablePlugin.ReactLoadablePlugin( - { - filename: _constants2.REACT_LOADABLE_MANIFEST - } ), !isServer && new _nextDropClientPagePlugin.DropClientPage(), // Moment.js is an extremely popular library that bundles large locale files - // by default due to how Webpack interprets its code. This is a practical - // solution that requires the user to opt into importing specific locales. - // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack - config.future.excludeDefaultMomentLocales && new _webpack.default.IgnorePlugin( /^\.\/locale$/, /moment$/ ), ...( dev ? ( () => { // Even though require.cache is server only we have to clear assets from both compilations - // This is because the client compilation generates the build manifest that's used on the server side - const - { - NextJsRequireCacheHotReloader - } = require( './webpack/plugins/nextjs-require-cache-hot-reloader' ); - const - { - UnlinkRemovedPagesPlugin - } = require( './webpack/plugins/unlink-removed-pages-plugin' ); - const devPlugins = [new UnlinkRemovedPagesPlugin(), new _webpack.default.NoEmitOnErrorsPlugin(), new NextJsRequireCacheHotReloader()]; - if ( !isServer ) { - const AutoDllPlugin = ( 0, _dllImport.importAutoDllPlugin )( - { - distDir - } ); - devPlugins.push( new AutoDllPlugin( - { - filename: '[name]_[hash].js', - path: './static/development/dll', - context: dir, - entry: - { - dll: ['react', 'react-dom'] - }, - config: - { - devtool, - mode: webpackMode, - resolve: resolveConfig - } - } ) ); - devPlugins.push( new _webpack.default.HotModuleReplacementPlugin() ); - } - return devPlugins; - } )() : [] ), !dev && new _webpack.default.HashedModuleIdsPlugin(), !dev && new _webpack.default.IgnorePlugin( - { - checkResource: resource => { - return /react-is/.test( resource ); - }, - checkContext: context => { - return /next-server[\\/]dist[\\/]/.test( context ) || /next[\\/]dist[\\/]/.test( context ); - } - } ), isServerless && isServer && new _serverlessPlugin.ServerlessPlugin(), isServer && new _pagesManifestPlugin.default( isLikeServerless ), target === 'server' && isServer && new _nextjsSsrModuleCache.default( - { - outputPath - } ), isServer && new _nextjsSsrImport.default(), !isServer && new _buildManifestPlugin.default( - { - buildId, - clientManifest: config.experimental.granularChunks, - modern: config.experimental.modern - } ), tracer && new _profilingPlugin.ProfilingPlugin( - { - tracer - } ), !isServer && useTypeScript && !ignoreTypeScriptErrors && new _forkTsCheckerWebpackPlugin.default( _pnpWebpackPlugin.default.forkTsCheckerOptions( - { - typescript: typeScriptPath, - async: dev, - useTypescriptIncrementalApi: true, - checkSyntacticErrors: true, - tsconfig: tsConfigPath, - reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'], - compilerOptions: - { - isolatedModules: true, - noEmit: true - }, - silent: true, - formatter: 'codeframe' - } ) ), config.experimental.modern && !isServer && !dev && new _nextEsmPlugin.default( - { - filename: getFileName => ( ...args ) => { - const name = typeof getFileName === 'function' ? getFileName( ...args ) : getFileName; - return name.includes( '.js' ) ? name.replace( /\.js$/, '.module.js' ) : escapePathVariables( args[0].chunk.name.replace( /\.js$/, '.module.js' ) ); - }, - chunkFilename: inputChunkName => inputChunkName.replace( /\.js$/, '.module.js' ) - } ), config.experimental.conformance && !dev && new _webpackConformancePlugin.default( - { - tests: [!isServer && conformanceConfig.MinificationConformanceCheck.enabled && new _webpackConformancePlugin.MinificationConformanceCheck(), conformanceConfig.ReactSyncScriptsConformanceCheck.enabled && new _webpackConformancePlugin.ReactSyncScriptsConformanceCheck( - { - AllowedSources: conformanceConfig.ReactSyncScriptsConformanceCheck.allowedSources || [] - } ), !isServer && conformanceConfig.DuplicatePolyfillsConformanceCheck.enabled && new _webpackConformancePlugin.DuplicatePolyfillsConformanceCheck( - { - BlockedAPIToBePolyfilled: conformanceConfig.DuplicatePolyfillsConformanceCheck.BlockedAPIToBePolyfilled - } )].filter( Boolean ) - } ) - ].filter( Boolean ) - }; - webpackConfig = await ( 0, _config.build )( webpackConfig, - { - rootDirectory: dir, - customAppFile, - isDevelopment: dev, - isServer, - hasSupportCss: !!config.experimental.css, - hasSupportScss: !!config.experimental.scss, - assetPrefix: config.assetPrefix || '', - sassOptions: config.experimental.sassOptions - } ); - if ( typeof config.webpack === 'function' ) { - webpackConfig = config.webpack( webpackConfig, - { - dir, - dev, - isServer, - buildId, - config, - defaultLoaders, - totalPages, - webpack: _webpack.default - } ); - if ( typeof webpackConfig.then === 'function' ) { - console.warn( '> Promise returned in next config. https://err.sh/zeit/next.js/promise-in-next-config' ); - } - } - - function canMatchCss( rule ) { - if ( !rule ) { - return false; - } - const fileNames = ['/tmp/test.css', '/tmp/test.scss', '/tmp/test.sass', '/tmp/test.less', '/tmp/test.styl']; - if ( rule instanceof RegExp && fileNames.some( input => rule.test( input ) ) ) { - return true; - } - if ( typeof rule === 'function' ) { - if ( fileNames.some( input => { - try { - if ( rule( input ) ) { - return true; - } - } - catch ( _ ) { } - return false; - } ) ) { - return true; - } - } - if ( Array.isArray( rule ) && rule.some( canMatchCss ) ) { - return true; - } - return false; - } - if ( config.experimental.css ) { - var _ref, _webpackConfig$module; - const hasUserCssConfig = ( _ref = ( _webpackConfig$module = webpackConfig.module ) === null || _webpackConfig$module === void 0 ? void 0 : _webpackConfig$module.rules.some( rule => canMatchCss( rule.test ) || canMatchCss( rule.include ) ) ) !== null && _ref !== void 0 ? _ref : false; - if ( hasUserCssConfig ) { - var _webpackConfig$module2, _webpackConfig$plugin, _webpackConfig$optimi, _webpackConfig$optimi2; // only show warning for one build - if ( isServer ) { - console.warn( _chalk.default.yellow.bold( 'Warning: ' ) + _chalk.default.bold( 'Built-in CSS support is being disabled due to custom CSS configuration being detected.\n' ) + 'See here for more info: https://err.sh/next.js/built-in-css-disabled\n' ); - } - if ( ( _webpackConfig$module2 = webpackConfig.module ) === null || _webpackConfig$module2 === void 0 ? void 0 : _webpackConfig$module2.rules.length ) { // Remove default CSS Loader - webpackConfig.module.rules = webpackConfig.module.rules.filter( r => { - var _r$oneOf, _r$oneOf$; - return !( typeof ( ( _r$oneOf = r.oneOf ) === null || _r$oneOf === void 0 ? void 0 : ( _r$oneOf$ = _r$oneOf[0] ) === null || _r$oneOf$ === void 0 ? void 0 : _r$oneOf$.options ) === 'object' && r.oneOf[0].options.__next_css_remove === true ); - } ); - } - if ( ( _webpackConfig$plugin = webpackConfig.plugins ) === null || _webpackConfig$plugin === void 0 ? void 0 : _webpackConfig$plugin.length ) { // Disable CSS Extraction Plugin - webpackConfig.plugins = webpackConfig.plugins.filter( p => p.__next_css_remove !== true ); - } - if ( ( _webpackConfig$optimi = webpackConfig.optimization ) === null || _webpackConfig$optimi === void 0 ? void 0 : ( _webpackConfig$optimi2 = _webpackConfig$optimi.minimizer ) === null || _webpackConfig$optimi2 === void 0 ? void 0 : _webpackConfig$optimi2.length ) { // Disable CSS Minifier - webpackConfig.optimization.minimizer = webpackConfig.optimization.minimizer.filter( e => e.__next_css_remove !== true ); - } - } - else { - await ( 0, _overrideCssConfiguration.__overrideCssConfiguration )( dir, !dev, webpackConfig ); - } - } // check if using @zeit/next-typescript and show warning - if ( isServer && webpackConfig.module && Array.isArray( webpackConfig.module.rules ) ) { - let foundTsRule = false; - webpackConfig.module.rules = webpackConfig.module.rules.filter( rule => { - if ( !( rule.test instanceof RegExp ) ) return true; - if ( 'noop.ts'.match( rule.test ) && !'noop.js'.match( rule.test ) ) { // remove if it matches @zeit/next-typescript - foundTsRule = rule.use === defaultLoaders.babel; - return !foundTsRule; - } - return true; - } ); - if ( foundTsRule ) { - console.warn( '\n@zeit/next-typescript is no longer needed since Next.js has built-in support for TypeScript now. Please remove it from your next.config.js and your .babelrc\n' ); - } - } // Patch `@zeit/next-sass`, `@zeit/next-less`, `@zeit/next-stylus` for compatibility - if ( webpackConfig.module && Array.isArray( webpackConfig.module.rules ) ) { - ; - [].forEach.call( webpackConfig.module.rules, function ( rule ) { - if ( !( rule.test instanceof RegExp && Array.isArray( rule.use ) ) ) { - return; - } - const isSass = rule.test.source === '\\.scss$' || rule.test.source === '\\.sass$'; - const isLess = rule.test.source === '\\.less$'; - const isCss = rule.test.source === '\\.css$'; - const isStylus = rule.test.source === '\\.styl$'; // Check if the rule we're iterating over applies to Sass, Less, or CSS - if ( !( isSass || isLess || isCss || isStylus ) ) { - return; - }; - [].forEach.call( rule.use, function ( use ) { - if ( !( use && typeof use === 'object' && ( // Identify use statements only pertaining to `css-loader` - use.loader === 'css-loader' || use.loader === 'css-loader/locals' ) && use.options && typeof use.options === 'object' && ( // The `minimize` property is a good heuristic that we need to - // perform this hack. The `minimize` property was only valid on - // old `css-loader` versions. Custom setups (that aren't next-sass, - // next-less or next-stylus) likely have the newer version. - // We still handle this gracefully below. - Object.prototype.hasOwnProperty.call( use.options, 'minimize' ) || Object.prototype.hasOwnProperty.call( use.options, 'exportOnlyLocals' ) ) ) ) { - return; - } // Try to monkey patch within a try-catch. We shouldn't fail the build - // if we cannot pull this off. - // The user may not even be using the `next-sass` or `next-less` or - // `next-stylus` plugins. - // If it does work, great! - try { // Resolve the version of `@zeit/next-css` as depended on by the Sass, - // Less or Stylus plugin. - const correctNextCss = ( 0, _resolveRequest.resolveRequest )( '@zeit/next-css', isCss ? // Resolve `@zeit/next-css` from the base directory - `${dir}/` : // Else, resolve it from the specific plugins - require.resolve( isSass ? '@zeit/next-sass' : isLess ? '@zeit/next-less' : isStylus ? '@zeit/next-stylus' : 'next' ) ); // If we found `@zeit/next-css` ... - if ( correctNextCss ) { // ... resolve the version of `css-loader` shipped with that - // package instead of whichever was hoisted highest in your - // `node_modules` tree. - const correctCssLoader = ( 0, _resolveRequest.resolveRequest )( use.loader, correctNextCss ); - if ( correctCssLoader ) { // We saved the user from a failed build! - use.loader = correctCssLoader; - } - } - } - catch ( _ ) { // The error is not required to be handled. - } - } ); - } ); - } // Backwards compat for `main.js` entry key - const originalEntry = webpackConfig.entry; - if ( typeof originalEntry !== 'undefined' ) { - webpackConfig.entry = async () => { - const entry = typeof originalEntry === 'function' ? await originalEntry() : originalEntry; // Server compilation doesn't have main.js - if ( clientEntries && entry['main.js'] && entry['main.js'].length > 0 ) { - const originalFile = clientEntries[_constants2.CLIENT_STATIC_FILES_RUNTIME_MAIN]; - entry[_constants2.CLIENT_STATIC_FILES_RUNTIME_MAIN] = [...entry['main.js'], originalFile]; - } - delete entry['main.js']; - return entry; - }; - } - if ( !dev ) { // entry is always a function - webpackConfig.entry = await webpackConfig.entry(); - } - - return webpackConfig; -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-build.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-build.js deleted file mode 100644 index 3310f74ec7..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-build.js +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env node - -"use strict"; -exports.__esModule = true; -exports.nextBuild = void 0; -var _fs = require( "fs" ); -var _index = _interopRequireDefault( require( "next/dist/compiled/arg/index.js" ) ); -var _path = require( "path" ); -var _build = _interopRequireDefault( require( "../build" ) ); -var _utils = require( "../server/lib/utils" ); -var _findPagesDir = require( "../lib/find-pages-dir" ); - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : - { - default: obj - }; -} -const nextBuild = argv => { - const args = ( 0, _index.default )( - { // Types - '--help': Boolean, - '--pagesDir': String, // Aliases - '-h': '--help' - }, - { - argv - } ); - if ( args['--help'] ) { - ( 0, _utils.printAndExit )( ` - Description - Compiles the application for production deployment - - Options - --pagesDir Location of pages dir - - Usage - $ next build - - represents where the compiled dist folder should go. - If no directory is provided, the dist folder will be created in the current directory. - You can set a custom folder in config https://github.com/zeit/next.js#custom-configuration, otherwise it will be created inside '.next' - `, 0 ); - } - const dir = ( 0, _path.resolve )( '.' ); - const destDir = ( 0, _path.resolve )( args._[0] || '.' ); - - args['--pagesDir'] && ( 0, _findPagesDir.setPagesDir )( ( 0, _path.join )( ( 0, _path.resolve )( '.' ), args['--pagesDir'] ) ); // Check if the provided directory exists - - if ( !( 0, _fs.existsSync )( dir ) ) { - ( 0, _utils.printAndExit )( `> No such directory exists as the project root: ${dir}` ); - } ( 0, _build.default )( dir, null, destDir ).then( () => process.exit( 0 ) ).catch( err => { - console.error( '' ); - console.error( '> Build error occurred' ); - ( 0, _utils.printAndExit )( err ); - } ); -}; -exports.nextBuild = nextBuild; \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-dev.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-dev.js deleted file mode 100644 index 88842029c0..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-dev.js +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env node - -"use strict"; -exports.__esModule = true; -exports.nextDev = void 0; -var _path = require( "path" ); -var _index = _interopRequireDefault( require( "next/dist/compiled/arg/index.js" ) ); -var _fs = require( "fs" ); -var _startServer = _interopRequireDefault( require( "../server/lib/start-server" ) ); -var _utils = require( "../server/lib/utils" ); -var _output = require( "../build/output" ); -var _findPagesDir = require( "../lib/find-pages-dir" ); - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : - { - default: obj - }; -} -const nextDev = argv => { - const args = ( 0, _index.default )( - { // Types - '--help': Boolean, - '--port': Number, - '--hostname': String, - '--pagesDir': String, // Aliases - '-h': '--help', - '-p': '--port', - '-H': '--hostname' - }, - { - argv - } ); - if ( args['--help'] ) { // tslint:disable-next-line - console.log( ` - Description - Starts the application in development mode (hot-code reloading, error - reporting, etc) - - Usage - $ next dev -p - - represents where the compiled folder should go. - If no directory is provided, the folder will be created in the current directory. - You can set a custom folder in config https://github.com/zeit/next.js#custom-configuration. - - Options - --port, -p A port number on which to start the application - --hostname, -H Hostname on which to start the application - --help, -h Displays this message - --pagesDir Location of pages dir - `); - process.exit( 0 ); - } - const dir = ( 0, _path.resolve )( '.' ); - args['--pagesDir'] && ( 0, _findPagesDir.setPagesDir )( ( 0, _path.join )( dir, args['--pagesDir'] ) ); // Check if pages dir exists and warn if not - if ( !( 0, _fs.existsSync )( dir ) ) { - ( 0, _utils.printAndExit )( `> No such directory exists as the project root: ${dir}` ); - } - const port = args['--port'] || 3000; - const appUrl = `http://${args['--hostname'] || 'localhost'}:${port}`; - ( 0, _output.startedDevelopmentServer )( appUrl ); - ( 0, _startServer.default )( - { - dir, - dev: true, - isNextDevCommand: true - }, port, args['--hostname'] ).then( async app => { - await app.prepare(); - } ).catch( err => { - if ( err.code === 'EADDRINUSE' ) { - let errorMessage = `Port ${port} is already in use.`; - const pkgAppPath = require( 'find-up' ).sync( 'package.json', - { - cwd: dir - } ); - const appPackage = require( pkgAppPath ); - if ( appPackage.scripts ) { - const nextScript = Object.entries( appPackage.scripts ).find( scriptLine => scriptLine[1] === 'next' ); - if ( nextScript ) { - errorMessage += `\nUse \`npm run ${nextScript[0]} -- -p \`.`; - } - } // tslint:disable-next-line - console.error( errorMessage ); - } - else { // tslint:disable-next-line - console.error( err ); - } - process.nextTick( () => process.exit( 1 ) ); - } ); -}; -exports.nextDev = nextDev; \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-export.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-export.js deleted file mode 100644 index f3ae18d658..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/cli/next-export.js +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env node -"use strict"; -exports.__esModule = true; -exports.nextExport = void 0; -var _path = require( "path" ); -var _fs = require( "fs" ); -var _index = _interopRequireDefault( require( "next/dist/compiled/arg/index.js" ) ); -var _export = _interopRequireDefault( require( "../export" ) ); -var _utils = require( "../server/lib/utils" ); -var _findPagesDir = require( "../lib/find-pages-dir" ); - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -const nextExport = argv => { - const args = ( 0, _index.default )( - { - // Types - '--help': Boolean, - '--silent': Boolean, - '--outdir': String, - '--threads': Number, - '--pagesDir': String, - - // Aliases - '-h': '--help', - '-s': '--silent', - '-o': '--outdir' - }, - - { argv } ); - - - if ( args['--help'] ) { - // tslint:disable-next-line - console.log( ` - Description - Exports the application for production deployment - - Usage - $ next export [options] - - represents where the compiled dist folder should go. - If no directory is provided, the 'out' folder will be created in the current directory. - - Options - -h - list this help - -o - set the output dir (defaults to 'out') - -s - do not print any messages to console - --pagesDir Location of pages dir - `); - process.exit( 0 ); - } - - const dir = ( 0, _path.resolve )( '.' ); - const destDir = ( 0, _path.resolve )( args._[0] || '.' ); - - args['--pagesDir'] && ( 0, _findPagesDir.setPagesDir )( ( 0, _path.join )( dir, args['--pagesDir'] ) ); - - // Check if pages dir exists and warn if not - if ( !( 0, _fs.existsSync )( dir ) ) { - ( 0, _utils.printAndExit )( `> No such directory exists as the project root: ${dir}` ); - } - - const options = { - silent: args['--silent'] || false, - threads: args['--threads'], - outdir: args['--outdir'] ? ( 0, _path.resolve )( args['--outdir'] ) : ( 0, _path.join )( dir, 'out' ) - }; - - ( 0, _export.default )( dir, options, null, destDir ). - then( () => { - ( 0, _utils.printAndExit )( 'Export successful', 0 ); - } ). - catch( err => { - ( 0, _utils.printAndExit )( err ); - } ); -}; exports.nextExport = nextExport; \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/export/index.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/export/index.js deleted file mode 100644 index 8dcffea9ec..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/export/index.js +++ /dev/null @@ -1,492 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.default = _default; -var _chalk = _interopRequireDefault( require( "chalk" ) ); -var _findUp = _interopRequireDefault( require( "find-up" ) ); -var _fs = require( "fs" ); - - - - - -var _jestWorker = _interopRequireDefault( require( "jest-worker" ) ); -var _mkdirp = _interopRequireDefault( require( "mkdirp" ) ); -var _os = require( "os" ); -var _path = require( "path" ); -var _util = require( "util" ); -var _index = require( "../build/output/index" ); -var _spinner = _interopRequireDefault( require( "../build/spinner" ) ); -var _constants = require( "../lib/constants" ); -var _recursiveCopy = require( "../lib/recursive-copy" ); -var _recursiveDelete = require( "../lib/recursive-delete" ); -var _constants2 = require( "../next-server/lib/constants" ); - - - - - - - - - - - -var _config = _interopRequireWildcard( require( "../next-server/server/config" ) ); - - -var _events = require( "../telemetry/events" ); -var _storage = require( "../telemetry/storage" ); -var _normalizePagePath = require( "../next-server/server/normalize-page-path" ); - -function _getRequireWildcardCache() { - if ( typeof WeakMap !== "function" ) return null; - var cache = new WeakMap(); - _getRequireWildcardCache = function () { - return cache; - }; - return cache; -} - -function _interopRequireWildcard( obj ) { - if ( obj && obj.__esModule ) { - return obj; - } - if ( obj === null || typeof obj !== "object" && typeof obj !== "function" ) { - return { - default: obj - }; - } - var cache = _getRequireWildcardCache(); - if ( cache && cache.has( obj ) ) { - return cache.get( obj ); - } - var newObj = {}; - var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; - for ( var key in obj ) { - if ( Object.prototype.hasOwnProperty.call( obj, key ) ) { - var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor( obj, key ) : null; - if ( desc && ( desc.get || desc.set ) ) { - Object.defineProperty( newObj, key, desc ); - } - else { - newObj[key] = obj[key]; - } - } - } - newObj.default = obj; - if ( cache ) { - cache.set( obj, newObj ); - } - return newObj; -} - -function _interopRequireDefault( obj ) { - return obj && obj.__esModule ? obj : - { - default: obj - }; -} - -const mkdirp = ( 0, _util.promisify )( _mkdirp.default ); -const copyFile = ( 0, _util.promisify )( _fs.copyFile ); - -const createProgress = ( total, label = 'Exporting' ) => { - let curProgress = 0; - let progressSpinner = ( 0, _spinner.default )( `${label} (${curProgress}/${total})`, - { - spinner: - { - frames: [ - '[ ]', - '[= ]', - '[== ]', - '[=== ]', - '[ ===]', - '[ ==]', - '[ =]', - '[ ]', - '[ =]', - '[ ==]', - '[ ===]', - '[====]', - '[=== ]', - '[== ]', - '[= ]' - ], - - interval: 80 - } - } ); - - - - return () => { - curProgress++; - - const newText = `${label} (${curProgress}/${total})`; - if ( progressSpinner ) { - progressSpinner.text = newText; - } - else { - console.log( newText ); - } - - if ( curProgress === total && progressSpinner ) { - progressSpinner.stop(); - console.log( newText ); - } - }; -}; - -async function _default( - dir, - options, - configuration, - destDir ) { - var _nextConfig$amp, _nextConfig$experimen; - - function log( message ) { - if ( options.silent ) { - return; - } - console.log( message ); - } - - dir = ( 0, _path.resolve )( dir ); - const nextConfig = configuration || ( 0, _config.default )( _constants2.PHASE_EXPORT, dir ); - const threads = options.threads || Math.max( ( 0, _os.cpus )().length - 1, 1 ); - const distDir = ( 0, _path.join )( destDir || dir, nextConfig.distDir ); - - const telemetry = options.buildExport ? null : new _storage.Telemetry( - { - distDir - } ); - - if ( telemetry ) { - telemetry.record( - ( 0, _events.eventCliSession )( _constants2.PHASE_EXPORT, distDir, - { - cliCommand: 'export', - isSrcDir: null, - hasNowJson: !!( await ( 0, _findUp.default )( 'now.json', - { - cwd: dir - } ) ), - isCustomServer: null - } ) ); - - - } - - const subFolders = nextConfig.exportTrailingSlash; - const isLikeServerless = nextConfig.target !== 'server'; - - log( `> using build directory: ${distDir}` ); - - if ( !( 0, _fs.existsSync )( distDir ) ) { - throw new Error( - `Build directory ${distDir} does not exist. Make sure you run "next build" before running "next start" or "next export".` ); - - } - - const buildId = ( 0, _fs.readFileSync )( ( 0, _path.join )( distDir, _constants2.BUILD_ID_FILE ), 'utf8' ); - const pagesManifest = !options.pages && - require( ( 0, _path.join )( - distDir, - isLikeServerless ? _constants2.SERVERLESS_DIRECTORY : _constants2.SERVER_DIRECTORY, - _constants2.PAGES_MANIFEST ) ); - - - let prerenderManifest; - try { - prerenderManifest = require( ( 0, _path.join )( distDir, _constants2.PRERENDER_MANIFEST ) ); - } - catch ( _ ) { } - - const distPagesDir = ( 0, _path.join )( - distDir, - isLikeServerless ? - _constants2.SERVERLESS_DIRECTORY : - ( 0, _path.join )( _constants2.SERVER_DIRECTORY, 'static', buildId ), - 'pages' ); - - - const pages = options.pages || Object.keys( pagesManifest ); - const defaultPathMap = {}; - - for ( const page of pages ) { - var _prerenderManifest; - // _document and _app are not real pages - // _error is exported as 404.html later on - // API Routes are Node.js functions - if ( - page === '/_document' || - page === '/_app' || - page === '/_error' || - page.match( _constants.API_ROUTE ) ) { - continue; - } - - // iSSG pages that are dynamic should not export templated version by - // default. In most cases, this would never work. There is no server that - // could run `getStaticProps`. If users make their page work lazily, they - // can manually add it to the `exportPathMap`. - if ( ( _prerenderManifest = prerenderManifest ) === null || _prerenderManifest === void 0 ? void 0 : _prerenderManifest.dynamicRoutes[page] ) { - continue; - } - - defaultPathMap[page] = { - page - }; - } - - // Initialize the output directory - // const outDir = options.outdir; - const outDir = destDir ? _path.join(destDir, 'out') : options.outdir; - - if ( outDir === ( 0, _path.join )( dir, 'public' ) ) { - throw new Error( - `The 'public' directory is reserved in Next.js and can not be used as the export out directory. https://err.sh/zeit/next.js/can-not-output-to-public` ); - - } - - await ( 0, _recursiveDelete.recursiveDelete )( ( 0, _path.join )( outDir ) ); - await mkdirp( ( 0, _path.join )( outDir, '_next', buildId ) ); - - ( 0, _fs.writeFileSync )( - ( 0, _path.join )( distDir, _constants2.EXPORT_DETAIL ), - JSON.stringify( - { - version: 1, - outDirectory: outDir, - success: false - } ), - - 'utf8' ); - - - // Copy static directory - if ( !options.buildExport && ( 0, _fs.existsSync )( ( 0, _path.join )( dir, 'static' ) ) ) { - log( ' copying "static" directory' ); - await ( 0, _recursiveCopy.recursiveCopy )( ( 0, _path.join )( dir, 'static' ), ( 0, _path.join )( outDir, 'static' ) ); - } - - // Copy .next/static directory - if ( ( 0, _fs.existsSync )( ( 0, _path.join )( distDir, _constants2.CLIENT_STATIC_FILES_PATH ) ) ) { - log( ' copying "static build" directory' ); - await ( 0, _recursiveCopy.recursiveCopy )( - ( 0, _path.join )( distDir, _constants2.CLIENT_STATIC_FILES_PATH ), - ( 0, _path.join )( outDir, '_next', _constants2.CLIENT_STATIC_FILES_PATH ) ); - - } - - // Get the exportPathMap from the config file - if ( typeof nextConfig.exportPathMap !== 'function' ) { - console.log( - `> No "exportPathMap" found in "${_constants2.CONFIG_FILE}". Generating map from "./pages"` ); - - nextConfig.exportPathMap = async defaultMap => { - return defaultMap; - }; - } - - // Start the rendering process - const renderOpts = { - dir, - buildId, - nextExport: true, - assetPrefix: nextConfig.assetPrefix.replace( /\/$/, '' ), - distDir, - dev: false, - staticMarkup: false, - hotReloader: null, - canonicalBase: ( ( _nextConfig$amp = nextConfig.amp ) === null || _nextConfig$amp === void 0 ? void 0 : _nextConfig$amp.canonicalBase ) || '', - isModern: nextConfig.experimental.modern, - ampValidatorPath: ( ( _nextConfig$experimen = nextConfig.experimental.amp ) === null || _nextConfig$experimen === void 0 ? void 0 : _nextConfig$experimen.validator ) || undefined - }; - - - const - { - serverRuntimeConfig, - publicRuntimeConfig - } = nextConfig; - - if ( Object.keys( publicRuntimeConfig ).length > 0 ) { - ; - renderOpts.runtimeConfig = publicRuntimeConfig; - } - - // We need this for server rendering the Link component. - ; - global.__NEXT_DATA__ = { - nextExport: true - }; - - - log( ` launching ${threads} workers` ); - const exportPathMap = await nextConfig.exportPathMap( defaultPathMap, - { - dev: false, - dir, - outDir, - distDir, - buildId - } ); - - if ( !exportPathMap['/404'] && !exportPathMap['/404.html'] ) { - exportPathMap['/404'] = exportPathMap['/404.html'] = { - page: '/_error' - }; - - } - const exportPaths = Object.keys( exportPathMap ); - const filteredPaths = exportPaths.filter( - // Remove API routes - route => !exportPathMap[route].page.match( _constants.API_ROUTE ) ); - - const hasApiRoutes = exportPaths.length !== filteredPaths.length; - - // Warn if the user defines a path for an API page - if ( hasApiRoutes ) { - log( - _chalk.default.yellow( - ' API pages are not supported by next export. https://err.sh/zeit/next.js/api-routes-static-export' ) ); - - - } - - const progress = !options.silent && createProgress( filteredPaths.length ); - const pagesDataDir = options.buildExport ? - outDir : - ( 0, _path.join )( outDir, '_next/data', buildId ); - - const ampValidations = {}; - let hadValidationError = false; - - const publicDir = ( 0, _path.join )( dir, _constants2.CLIENT_PUBLIC_FILES_PATH ); - // Copy public directory - if ( !options.buildExport && ( 0, _fs.existsSync )( publicDir ) ) { - log( ' copying "public" directory' ); - await ( 0, _recursiveCopy.recursiveCopy )( publicDir, outDir, - { - filter( path ) { - // Exclude paths used by pages - return !exportPathMap[path]; - } - } ); - - } - - const worker = new _jestWorker.default( - require.resolve( './worker' ), - { - maxRetries: 0, - numWorkers: threads, - enableWorkerThreads: nextConfig.experimental.workerThreads, - exposedMethods: ['default'] - } ); - - - - worker.getStdout().pipe( process.stdout ); - worker.getStderr().pipe( process.stderr ); - - let renderError = false; - - await Promise.all( - filteredPaths.map( async path => { - const result = await worker.default( - { - path, - pathMap: exportPathMap[path], - distDir, - buildId, - outDir, - pagesDataDir, - renderOpts, - serverRuntimeConfig, - subFolders, - buildExport: options.buildExport, - serverless: ( 0, _config.isTargetLikeServerless )( nextConfig.target ) - } ); - - - for ( const validation of result.ampValidations || [] ) { - const - { - page, - result - } = validation; - ampValidations[page] = result; - hadValidationError = - hadValidationError || - Array.isArray( result === null || result === void 0 ? void 0 : result.errors ) && result.errors.length > 0; - } - renderError = renderError || !!result.error; - - if ( - options.buildExport && - typeof result.fromBuildExportRevalidate !== 'undefined' ) { - configuration.initialPageRevalidationMap[path] = - result.fromBuildExportRevalidate; - } - if ( progress ) progress(); - } ) ); - - - worker.end(); - - // copy prerendered routes to outDir - if ( !options.buildExport && prerenderManifest ) { - await Promise.all( - Object.keys( prerenderManifest.routes ).map( async route => { - route = ( 0, _normalizePagePath.normalizePagePath )( route ); - const orig = ( 0, _path.join )( distPagesDir, route ); - const htmlDest = ( 0, _path.join )( - outDir, - `${route}${ - subFolders && route !== '/index' ? `${_path.sep}index` : '' - }.html` ); - - const jsonDest = ( 0, _path.join )( pagesDataDir, `${route}.json` ); - - await mkdirp( ( 0, _path.dirname )( htmlDest ) ); - await mkdirp( ( 0, _path.dirname )( jsonDest ) ); - await copyFile( `${orig}.html`, htmlDest ); - await copyFile( `${orig}.json`, jsonDest ); - } ) ); - - } - - if ( Object.keys( ampValidations ).length ) { - console.log( ( 0, _index.formatAmpMessages )( ampValidations ) ); - } - if ( hadValidationError ) { - throw new Error( - `AMP Validation caused the export to fail. https://err.sh/zeit/next.js/amp-export-validation` ); - - } - - if ( renderError ) { - throw new Error( `Export encountered errors` ); - } - // Add an empty line to the console for the better readability. - log( '' ); - - ( 0, _fs.writeFileSync )( - ( 0, _path.join )( distDir, _constants2.EXPORT_DETAIL ), - JSON.stringify( - { - version: 1, - outDirectory: outDir, - success: true - } ), - - 'utf8' ); - - - if ( telemetry ) { - await telemetry.flush(); - } -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/lib/find-pages-dir.js b/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/lib/find-pages-dir.js deleted file mode 100644 index f3c00c18eb..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.3.1/dist/lib/find-pages-dir.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -exports.__esModule = true; -exports.setPagesDir = setPagesDir; -exports.findPagesDir = findPagesDir; -var _fs = _interopRequireDefault(require("fs")); -var _path = _interopRequireDefault(require("path")); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} -let pagesDir = null; -const existsSync = f => { - try { - _fs.default.accessSync(f, _fs.default.constants.F_OK); - return true; - } catch (_) { - return false; - } -}; - -function setPagesDir(dir) { - pagesDir = dir; - console.log('set dir', pagesDir); -} - -function findPagesDir(dir) { - if (pagesDir) { - console.log('return', pagesDir); - return pagesDir; - } // prioritize ./pages over ./src/pages - let curDir = _path.default.join(dir, 'pages'); - if (existsSync(curDir)) { - console.log('return', curDir); - return curDir; - } - curDir = _path.default.join(dir, 'src/pages'); - if (existsSync(curDir)) { - console.log('return', curDir); - return curDir; - } // Check one level up the tree to see if the pages directory might be there - if (existsSync(_path.default.join(dir, '..', 'pages'))) { - throw new Error('> No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?'); - } - throw new Error("> Couldn't find a `pages` directory. Please create one under the project root"); -} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/entries.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/entries.js deleted file mode 100644 index 95c0610f19..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/entries.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict";exports.__esModule=true;exports.createPagesMapping=createPagesMapping;exports.createEntrypoints=createEntrypoints;var _chalk=_interopRequireDefault(require("next/dist/compiled/chalk"));var _path=require("path");var _querystring=require("querystring");var _constants=require("../lib/constants");var _config=require("../next-server/server/config");var _normalizePagePath=require("../next-server/server/normalize-page-path");var _log=require("./output/log");function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function createPagesMapping(pagePaths,extensions){const previousPages={};const pages=pagePaths.reduce((result,pagePath)=>{let page=`${pagePath.replace(new RegExp(`\\.+(${extensions.join('|')})$`),'').replace(/\\/g,'/')}`.replace(/\/index$/,'');page=page==='/index'?'/':page;const pageKey=page===''?'/':page;if(pageKey in result){(0,_log.warn)(`Duplicate page detected. ${_chalk.default.cyan((0,_path.join)('pages',previousPages[pageKey]))} and ${_chalk.default.cyan((0,_path.join)('pages',pagePath))} both resolve to ${_chalk.default.cyan(pageKey)}.`);}else{previousPages[pageKey]=pagePath;}result[pageKey]=(0,_path.join)(_constants.PAGES_DIR_ALIAS,pagePath).replace(/\\/g,'/');return result;},{});pages['/_app']=pages['/_app']||'next/dist/pages/_app';pages['/_error']=pages['/_error']||'next/dist/pages/_error';pages['/_document']=pages['/_document']||'next/dist/pages/_document';Object.keys(pages).forEach(key=>{if(key.includes('.'))delete pages[key];});return pages;}function createEntrypoints(dev,pages,target,buildId,previewMode,config,loadedEnvFiles){const client={};const server={};const hasRuntimeConfig=Object.keys(config.publicRuntimeConfig).length>0||Object.keys(config.serverRuntimeConfig).length>0;const defaultServerlessOptions={absoluteAppPath:pages['/_app'],absoluteDocumentPath:pages['/_document'],absoluteErrorPath:pages['/_error'],distDir:_constants.DOT_NEXT_ALIAS,buildId,assetPrefix:config.assetPrefix,generateEtags:config.generateEtags,canonicalBase:config.canonicalBase,basePath:config.experimental.basePath,runtimeConfig:hasRuntimeConfig?JSON.stringify({publicRuntimeConfig:config.publicRuntimeConfig,serverRuntimeConfig:config.serverRuntimeConfig}):'',previewProps:JSON.stringify(previewMode),loadedEnvFiles:JSON.stringify(loadedEnvFiles)};Object.keys(pages).forEach(page=>{const absolutePagePath=pages[page];const bundleFile=`${(0,_normalizePagePath.normalizePagePath)(page)}.js`;const isApiRoute=page.match(_constants.API_ROUTE);const bundlePath=(0,_path.join)('static',buildId,'pages',bundleFile);const isLikeServerless=(0,_config.isTargetLikeServerless)(target);if(isApiRoute&&isLikeServerless){const serverlessLoaderOptions={page,absolutePagePath,...defaultServerlessOptions};server[(0,_path.join)('pages',bundleFile)]=`next-serverless-loader?${(0,_querystring.stringify)(serverlessLoaderOptions)}!`;}else if(isApiRoute||target==='server'){server[bundlePath]=[absolutePagePath];}else if(isLikeServerless&&page!=='/_app'&&page!=='/_document'){const serverlessLoaderOptions={page,absolutePagePath,...defaultServerlessOptions};server[(0,_path.join)('pages',bundleFile)]=`next-serverless-loader?${(0,_querystring.stringify)(serverlessLoaderOptions)}!`;}if(page==='/_document'){return;}if(!isApiRoute){const pageLoaderOpts={page,absolutePagePath};const pageLoader=`next-client-pages-loader?${(0,_querystring.stringify)(pageLoaderOpts)}!`;// Make sure next/router is a dependency of _app or else granularChunks -// might cause the router to not be able to load causing hydration -// to fail -client[bundlePath]=page==='/_app'?[pageLoader,require.resolve('../client/router')]:pageLoader;}});return{client,server};} -//# sourceMappingURL=entries.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/index.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/index.js deleted file mode 100644 index 25d72a2280..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/build/index.js +++ /dev/null @@ -1,50 +0,0 @@ -"use strict";exports.__esModule=true;exports.default=build;require("../next-server/server/node-polyfill-fetch");var _crypto=_interopRequireDefault(require("crypto"));var _fs=require("fs");var _jestWorker=_interopRequireDefault(require("jest-worker"));var _chalk=_interopRequireDefault(require("next/dist/compiled/chalk"));var _devalue=_interopRequireDefault(require("next/dist/compiled/devalue"));var _escapeStringRegexp=_interopRequireDefault(require("next/dist/compiled/escape-string-regexp"));var _findUp=_interopRequireDefault(require("next/dist/compiled/find-up"));var _index=_interopRequireDefault(require("next/dist/compiled/nanoid/index.js"));var _pathToRegexp=require("next/dist/compiled/path-to-regexp");var _path=_interopRequireDefault(require("path"));var _formatWebpackMessages=_interopRequireDefault(require("../client/dev/error-overlay/format-webpack-messages"));var _checkCustomRoutes=_interopRequireWildcard(require("../lib/check-custom-routes"));var _constants=require("../lib/constants");var _findPagesDir=require("../lib/find-pages-dir");var _loadEnvConfig=require("../lib/load-env-config");var _recursiveDelete=require("../lib/recursive-delete");var _recursiveReaddir=require("../lib/recursive-readdir");var _verifyTypeScriptSetup=require("../lib/verifyTypeScriptSetup");var _constants2=require("../next-server/lib/constants");var _utils=require("../next-server/lib/router/utils");var _config=_interopRequireWildcard(require("../next-server/server/config"));var _normalizePagePath=require("../next-server/server/normalize-page-path");var ciEnvironment=_interopRequireWildcard(require("../telemetry/ci-info"));var _events=require("../telemetry/events");var _storage=require("../telemetry/storage");var _compiler=require("./compiler");var _entries=require("./entries");var _generateBuildId=require("./generate-build-id");var _isWriteable=require("./is-writeable");var _spinner=_interopRequireDefault(require("./spinner"));var _utils2=require("./utils");var _webpackConfig=_interopRequireDefault(require("./webpack-config"));var _writeBuildId=require("./write-build-id");function _getRequireWildcardCache(){if(typeof WeakMap!=="function")return null;var cache=new WeakMap();_getRequireWildcardCache=function(){return cache;};return cache;}function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj;}if(obj===null||typeof obj!=="object"&&typeof obj!=="function"){return{default:obj};}var cache=_getRequireWildcardCache();if(cache&&cache.has(obj)){return cache.get(obj);}var newObj={};var hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;if(desc&&(desc.get||desc.set)){Object.defineProperty(newObj,key,desc);}else{newObj[key]=obj[key];}}}newObj.default=obj;if(cache){cache.set(obj,newObj);}return newObj;}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _wrapRegExp(re,groups){_wrapRegExp=function(re,groups){return new BabelRegExp(re,undefined,groups);};var _RegExp=_wrapNativeSuper(RegExp);var _super=RegExp.prototype;var _groups=new WeakMap();function BabelRegExp(re,flags,groups){var _this=_RegExp.call(this,re,flags);_groups.set(_this,groups||_groups.get(re));return _this;}_inherits(BabelRegExp,_RegExp);BabelRegExp.prototype.exec=function(str){var result=_super.exec.call(this,str);if(result)result.groups=buildGroups(result,this);return result;};BabelRegExp.prototype[Symbol.replace]=function(str,substitution){if(typeof substitution==="string"){var groups=_groups.get(this);return _super[Symbol.replace].call(this,str,substitution.replace(/\$<([^>]+)>/g,function(_,name){return"$"+groups[name];}));}else if(typeof substitution==="function"){var _this=this;return _super[Symbol.replace].call(this,str,function(){var args=[];args.push.apply(args,arguments);if(typeof args[args.length-1]!=="object"){args.push(buildGroups(args,_this));}return substitution.apply(this,args);});}else{return _super[Symbol.replace].call(this,str,substitution);}};function buildGroups(result,re){var g=_groups.get(re);return Object.keys(g).reduce(function(groups,name){groups[name]=result[g[name]];return groups;},Object.create(null));}return _wrapRegExp.apply(this,arguments);}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function");}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,writable:true,configurable:true}});if(superClass)_setPrototypeOf(subClass,superClass);}function _possibleConstructorReturn(self,call){if(call&&(typeof call==="object"||typeof call==="function")){return call;}return _assertThisInitialized(self);}function _assertThisInitialized(self){if(self===void 0){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return self;}function _wrapNativeSuper(Class){var _cache=typeof Map==="function"?new Map():undefined;_wrapNativeSuper=function _wrapNativeSuper(Class){if(Class===null||!_isNativeFunction(Class))return Class;if(typeof Class!=="function"){throw new TypeError("Super expression must either be null or a function");}if(typeof _cache!=="undefined"){if(_cache.has(Class))return _cache.get(Class);_cache.set(Class,Wrapper);}function Wrapper(){return _construct(Class,arguments,_getPrototypeOf(this).constructor);}Wrapper.prototype=Object.create(Class.prototype,{constructor:{value:Wrapper,enumerable:false,writable:true,configurable:true}});return _setPrototypeOf(Wrapper,Class);};return _wrapNativeSuper(Class);}function _construct(Parent,args,Class){if(_isNativeReflectConstruct()){_construct=Reflect.construct;}else{_construct=function _construct(Parent,args,Class){var a=[null];a.push.apply(a,args);var Constructor=Function.bind.apply(Parent,a);var instance=new Constructor();if(Class)_setPrototypeOf(instance,Class.prototype);return instance;};}return _construct.apply(null,arguments);}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true;}catch(e){return false;}}function _isNativeFunction(fn){return Function.toString.call(fn).indexOf("[native code]")!==-1;}function _setPrototypeOf(o,p){_setPrototypeOf=Object.setPrototypeOf||function _setPrototypeOf(o,p){o.__proto__=p;return o;};return _setPrototypeOf(o,p);}function _getPrototypeOf(o){_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function _getPrototypeOf(o){return o.__proto__||Object.getPrototypeOf(o);};return _getPrototypeOf(o);}const staticCheckWorker=require.resolve('./utils');async function build(dir,conf=null,destDir){var _pageKeys$find,_config$experimental,_namedExports$include,_namedExports;if(!(await(0,_isWriteable.isWriteable)(dir))){throw new Error('> Build directory is not writeable. https://err.sh/vercel/next.js/build-dir-not-writeable');}// attempt to load global env values so they are available in next.config.js -const{loadedEnvFiles}=(0,_loadEnvConfig.loadEnvConfig)(dir);const config=(0,_config.default)(_constants2.PHASE_PRODUCTION_BUILD,dir,conf);const{target}=config;const buildId=await(0,_generateBuildId.generateBuildId)(config.generateBuildId,_index.default);const distDir=_path.default.join(destDir,config.distDir);const headers=[];const rewrites=[];const redirects=[];if(typeof config.experimental.redirects==='function'){const _redirects=await config.experimental.redirects();(0,_checkCustomRoutes.default)(_redirects,'redirect');redirects.push(..._redirects);}if(typeof config.experimental.rewrites==='function'){const _rewrites=await config.experimental.rewrites();(0,_checkCustomRoutes.default)(_rewrites,'rewrite');rewrites.push(..._rewrites);}if(typeof config.experimental.headers==='function'){const _headers=await config.experimental.headers();(0,_checkCustomRoutes.default)(_headers,'header');headers.push(..._headers);}if(ciEnvironment.isCI&&!ciEnvironment.hasNextSupport){const cacheDir=_path.default.join(distDir,'cache');const hasCache=await _fs.promises.access(cacheDir).then(()=>true).catch(()=>false);if(!hasCache){// Intentionally not piping to stderr in case people fail in CI when -// stderr is detected. -console.log(_chalk.default.bold.yellow(`Warning: `)+_chalk.default.bold(`No build cache found. Please configure build caching for faster rebuilds. Read more: https://err.sh/next.js/no-cache`));console.log('');}}const buildSpinner=(0,_spinner.default)({prefixText:'Creating an optimized production build'});const telemetry=new _storage.Telemetry({distDir});const publicDir=_path.default.join(dir,'public');const pagesDir=(0,_findPagesDir.findPagesDir)(dir);let publicFiles=[];let hasPublicDir=false;telemetry.record((0,_events.eventCliSession)(_constants2.PHASE_PRODUCTION_BUILD,dir,{cliCommand:'build',isSrcDir:_path.default.relative(dir,pagesDir).startsWith('src'),hasNowJson:!!(await(0,_findUp.default)('now.json',{cwd:dir})),isCustomServer:null}));(0,_events.eventNextPlugins)(_path.default.resolve(dir)).then(events=>telemetry.record(events));await(0,_verifyTypeScriptSetup.verifyTypeScriptSetup)(dir,pagesDir);try{await _fs.promises.stat(publicDir);hasPublicDir=true;}catch(_){}if(hasPublicDir){publicFiles=await(0,_recursiveReaddir.recursiveReadDir)(publicDir,/.*/);}let tracer=null;if(config.experimental.profiling){const{createTrace}=require('./profiler/profiler.js');tracer=createTrace(_path.default.join(distDir,`profile-events.json`));tracer.profiler.startProfiling();}const isLikeServerless=(0,_config.isTargetLikeServerless)(target);const pagePaths=await(0,_utils2.collectPages)(pagesDir,config.pageExtensionsRnv||config.pageExtensions);// needed for static exporting since we want to replace with HTML -// files -const allStaticPages=new Set();let allPageInfos=new Map();const previewProps={previewModeId:_crypto.default.randomBytes(16).toString('hex'),previewModeSigningKey:_crypto.default.randomBytes(32).toString('hex'),previewModeEncryptionKey:_crypto.default.randomBytes(32).toString('hex')};const mappedPages=(0,_entries.createPagesMapping)(pagePaths,config.pageExtensions);const entrypoints=(0,_entries.createEntrypoints)(/* dev */false,mappedPages,target,buildId,previewProps,config,loadedEnvFiles);const pageKeys=Object.keys(mappedPages);const conflictingPublicFiles=[];const hasCustomErrorPage=mappedPages['/_error'].startsWith('private-next-pages');const hasPages404=Boolean(mappedPages['/404']&&mappedPages['/404'].startsWith('private-next-pages'));let hasNonStaticErrorPage;if(hasPublicDir){try{await _fs.promises.stat(_path.default.join(publicDir,'_next'));throw new Error(_constants.PUBLIC_DIR_MIDDLEWARE_CONFLICT);}catch(err){}}for(let file of publicFiles){file=file.replace(/\\/g,'/').replace(/\/index$/,'').split(publicDir).pop();if(mappedPages[file]){conflictingPublicFiles.push(file);}}const numConflicting=conflictingPublicFiles.length;if(numConflicting){throw new Error(`Conflicting public and page file${numConflicting===1?' was':'s were'} found. https://err.sh/vercel/next.js/conflicting-public-file-page\n${conflictingPublicFiles.join('\n')}`);}const nestedReservedPages=pageKeys.filter(page=>{return page.match(/\/(_app|_document|_error)$/)&&_path.default.dirname(page)!=='/';});if(nestedReservedPages.length){console.warn('\n'+_chalk.default.bold.yellow(`Warning: `)+_chalk.default.bold(`The following reserved Next.js pages were detected not directly under the pages directory:\n`)+nestedReservedPages.join('\n')+_chalk.default.bold(`\nSee more info here: https://err.sh/next.js/nested-reserved-page\n`));}const buildCustomRoute=(r,type)=>{const keys=[];const routeRegex=(0,_pathToRegexp.pathToRegexp)(r.source,keys,{strict:true,sensitive:false,delimiter:'/'// default is `/#?`, but Next does not pass query info -});return{...r,...(type==='redirect'?{statusCode:(0,_checkCustomRoutes.getRedirectStatus)(r),permanent:undefined}:{}),regex:routeRegex.source};};const firstOptionalCatchAllPage=(_pageKeys$find=pageKeys.find(f=>/\[\[\.{3}[^\][/]*\]\]/.test(f)))!==null&&_pageKeys$find!==void 0?_pageKeys$find:null;if(((_config$experimental=config.experimental)===null||_config$experimental===void 0?void 0:_config$experimental.optionalCatchAll)!==true&&firstOptionalCatchAllPage){const msg=`Optional catch-all routes are currently experimental and cannot be used by default ("${firstOptionalCatchAllPage}").`;throw new Error(msg);}const routesManifestPath=_path.default.join(distDir,_constants2.ROUTES_MANIFEST);const routesManifest={version:1,pages404:true,basePath:config.experimental.basePath,redirects:redirects.map(r=>buildCustomRoute(r,'redirect')),rewrites:rewrites.map(r=>buildCustomRoute(r,'rewrite')),headers:headers.map(r=>buildCustomRoute(r,'header')),dynamicRoutes:(0,_utils.getSortedRoutes)(pageKeys).filter(_utils.isDynamicRoute).map(page=>{const routeRegex=(0,_utils.getRouteRegex)(page);return{page,regex:routeRegex.re.source,namedRegex:routeRegex.namedRegex,routeKeys:Object.keys(routeRegex.groups)};})};await _fs.promises.mkdir(distDir,{recursive:true});// We need to write the manifest with rewrites before build -// so serverless can import the manifest -await _fs.promises.writeFile(routesManifestPath,JSON.stringify(routesManifest),'utf8');const configs=await Promise.all([(0,_webpackConfig.default)(destDir||dir,{tracer,buildId,isServer:false,config,target,pagesDir,entrypoints:entrypoints.client}),(0,_webpackConfig.default)(destDir||dir,{tracer,buildId,isServer:true,config,target,pagesDir,entrypoints:entrypoints.server})]);const clientConfig=configs[0];if(clientConfig.optimization&&(clientConfig.optimization.minimize!==true||clientConfig.optimization.minimizer&&clientConfig.optimization.minimizer.length===0)){console.warn(_chalk.default.bold.yellow(`Warning: `)+_chalk.default.bold(`Production code optimization has been disabled in your project. Read more: https://err.sh/vercel/next.js/minification-disabled`));}const webpackBuildStart=process.hrtime();let result={warnings:[],errors:[]};// TODO: why do we need this?? https://github.com/vercel/next.js/issues/8253 -if(isLikeServerless){const clientResult=await(0,_compiler.runCompiler)(clientConfig);// Fail build if clientResult contains errors -if(clientResult.errors.length>0){result={warnings:[...clientResult.warnings],errors:[...clientResult.errors]};}else{const serverResult=await(0,_compiler.runCompiler)(configs[1]);result={warnings:[...clientResult.warnings,...serverResult.warnings],errors:[...clientResult.errors,...serverResult.errors]};}}else{result=await(0,_compiler.runCompiler)(configs);}const webpackBuildEnd=process.hrtime(webpackBuildStart);if(buildSpinner){buildSpinner.stopAndPersist();}console.log();result=(0,_formatWebpackMessages.default)(result);if(result.errors.length>0){// Only keep the first error. Others are often indicative -// of the same problem, but confuse the reader with noise. -if(result.errors.length>1){result.errors.length=1;}const error=result.errors.join('\n\n');console.error(_chalk.default.red('Failed to compile.\n'));if(error.indexOf('private-next-pages')>-1&&error.indexOf('does not contain a default export')>-1){const page_name_regex=/*#__PURE__*/_wrapRegExp(/'private\x2Dnext\x2Dpages\/([\0-&\(-\uFFFF]*)'/,{page_name:1});const parsed=page_name_regex.exec(error);const page_name=parsed&&parsed.groups&&parsed.groups.page_name;throw new Error(`webpack build failed: found page without a React Component as default export in pages/${page_name}\n\nSee https://err.sh/vercel/next.js/page-without-valid-component for more info.`);}console.error(error);console.error();if(error.indexOf('private-next-pages')>-1||error.indexOf('__next_polyfill__')>-1){throw new Error('> webpack config.resolve.alias was incorrectly overriden. https://err.sh/vercel/next.js/invalid-resolve-alias');}throw new Error('> Build failed because of webpack errors');}else{telemetry.record((0,_events.eventBuildCompleted)(pagePaths,{durationInSeconds:webpackBuildEnd[0]}));if(result.warnings.length>0){console.warn(_chalk.default.yellow('Compiled with warnings.\n'));console.warn(result.warnings.join('\n\n'));console.warn();}else{console.log(_chalk.default.green('Compiled successfully.\n'));}}const postBuildSpinner=(0,_spinner.default)({prefixText:'Automatically optimizing pages'});const manifestPath=_path.default.join(distDir,isLikeServerless?_constants2.SERVERLESS_DIRECTORY:_constants2.SERVER_DIRECTORY,_constants2.PAGES_MANIFEST);const buildManifestPath=_path.default.join(distDir,_constants2.BUILD_MANIFEST);const ssgPages=new Set();const ssgFallbackPages=new Set();const staticPages=new Set();const invalidPages=new Set();const hybridAmpPages=new Set();const serverPropsPages=new Set();const additionalSsgPaths=new Map();const pageInfos=new Map();const pagesManifest=JSON.parse(await _fs.promises.readFile(manifestPath,'utf8'));const buildManifest=JSON.parse(await _fs.promises.readFile(buildManifestPath,'utf8'));let customAppGetInitialProps;let namedExports;process.env.NEXT_PHASE=_constants2.PHASE_PRODUCTION_BUILD;const staticCheckWorkers=new _jestWorker.default(staticCheckWorker,{numWorkers:config.experimental.cpus,enableWorkerThreads:config.experimental.workerThreads});staticCheckWorkers.getStdout().pipe(process.stdout);staticCheckWorkers.getStderr().pipe(process.stderr);const runtimeEnvConfig={publicRuntimeConfig:config.publicRuntimeConfig,serverRuntimeConfig:config.serverRuntimeConfig};hasNonStaticErrorPage=hasCustomErrorPage&&(await(0,_utils2.hasCustomGetInitialProps)(_path.default.join(distDir,...(isLikeServerless?['serverless','pages']:['server','static',buildId,'pages']),'_error.js'),runtimeEnvConfig,false));const analysisBegin=process.hrtime();await Promise.all(pageKeys.map(async page=>{const actualPage=(0,_normalizePagePath.normalizePagePath)(page);const[selfSize,allSize]=await(0,_utils2.getJsPageSizeInKb)(actualPage,distDir,buildId,buildManifest,config.experimental.modern);const bundleRelative=_path.default.join(isLikeServerless?'pages':`static/${buildId}/pages`,actualPage+'.js');const serverBundle=_path.default.join(distDir,isLikeServerless?_constants2.SERVERLESS_DIRECTORY:_constants2.SERVER_DIRECTORY,bundleRelative);let isSsg=false;let isStatic=false;let isHybridAmp=false;let ssgPageRoutes=null;let hasSsgFallback=false;pagesManifest[page]=bundleRelative.replace(/\\/g,'/');const nonReservedPage=!page.match(/^\/(_app|_error|_document|api)/);if(nonReservedPage&&customAppGetInitialProps===undefined){customAppGetInitialProps=(0,_utils2.hasCustomGetInitialProps)(isLikeServerless?serverBundle:_path.default.join(distDir,_constants2.SERVER_DIRECTORY,`/static/${buildId}/pages/_app.js`),runtimeEnvConfig,true);namedExports=(0,_utils2.getNamedExports)(isLikeServerless?serverBundle:_path.default.join(distDir,_constants2.SERVER_DIRECTORY,`/static/${buildId}/pages/_app.js`),runtimeEnvConfig);if(customAppGetInitialProps){console.warn(_chalk.default.bold.yellow(`Warning: `)+_chalk.default.yellow(`You have opted-out of Automatic Static Optimization due to \`getInitialProps\` in \`pages/_app\`. This does not opt-out pages with \`getStaticProps\``));console.warn('Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n');}}if(nonReservedPage){try{let result=await staticCheckWorkers.isPageStatic(page,serverBundle,runtimeEnvConfig);if(result.isHybridAmp){isHybridAmp=true;hybridAmpPages.add(page);}if(result.isAmpOnly){// ensure all AMP only bundles got removed -try{const clientBundle=_path.default.join(distDir,'static',buildId,'pages',actualPage+'.js');await _fs.promises.unlink(clientBundle);if(config.experimental.modern){await _fs.promises.unlink(clientBundle.replace(/\.js$/,'.module.js'));}}catch(err){if(err.code!=='ENOENT'){throw err;}}}if(result.hasStaticProps){ssgPages.add(page);isSsg=true;if(result.prerenderRoutes){additionalSsgPaths.set(page,result.prerenderRoutes);ssgPageRoutes=result.prerenderRoutes;}if(result.prerenderFallback){hasSsgFallback=true;ssgFallbackPages.add(page);}}else if(result.hasServerProps){serverPropsPages.add(page);}else if(result.isStatic&&customAppGetInitialProps===false){staticPages.add(page);isStatic=true;}if(hasPages404&&page==='/404'){if(!result.isStatic&&!result.hasStaticProps){throw new Error(_constants.PAGES_404_GET_INITIAL_PROPS_ERROR);}// we need to ensure the 404 lambda is present since we use -// it when _app has getInitialProps -if(customAppGetInitialProps&&!result.hasStaticProps){staticPages.delete(page);}}}catch(err){if(err.message!=='INVALID_DEFAULT_EXPORT')throw err;invalidPages.add(page);}}pageInfos.set(page,{size:selfSize,totalSize:allSize,serverBundle,static:isStatic,isSsg,isHybridAmp,ssgPageRoutes,hasSsgFallback});}));staticCheckWorkers.end();if(serverPropsPages.size>0||ssgPages.size>0){// We update the routes manifest after the build with the -// data routes since we can't determine these until after build -routesManifest.dataRoutes=(0,_utils.getSortedRoutes)([...serverPropsPages,...ssgPages]).map(page=>{const pagePath=(0,_normalizePagePath.normalizePagePath)(page);const dataRoute=_path.default.posix.join('/_next/data',buildId,`${pagePath}.json`);let dataRouteRegex;let routeKeys;let namedDataRouteRegex;if((0,_utils.isDynamicRoute)(page)){const routeRegex=(0,_utils.getRouteRegex)(dataRoute.replace(/\.json$/,''));dataRouteRegex=routeRegex.re.source.replace(/\(\?:\\\/\)\?\$$/,'\\.json$');namedDataRouteRegex=routeRegex.namedRegex.replace(/\(\?:\/\)\?\$$/,'\\.json$');routeKeys=Object.keys(routeRegex.groups);}else{dataRouteRegex=new RegExp(`^${_path.default.posix.join('/_next/data',(0,_escapeStringRegexp.default)(buildId),`${pagePath}.json`)}$`).source;}return{page,routeKeys,dataRouteRegex,namedDataRouteRegex};});await _fs.promises.writeFile(routesManifestPath,JSON.stringify(routesManifest),'utf8');}// Since custom _app.js can wrap the 404 page we have to opt-out of static optimization if it has getInitialProps -// Only export the static 404 when there is no /_error present -const useStatic404=!customAppGetInitialProps&&(!hasNonStaticErrorPage||hasPages404);if(invalidPages.size>0){throw new Error(`Build optimization failed: found page${invalidPages.size===1?'':'s'} without a React Component as default export in \n${[...invalidPages].map(pg=>`pages${pg}`).join('\n')}\n\nSee https://err.sh/vercel/next.js/page-without-valid-component for more info.\n`);}if(Array.isArray(configs[0].plugins)){configs[0].plugins.some(plugin=>{if(!plugin.ampPages){return false;}plugin.ampPages.forEach(pg=>{pageInfos.get(pg).isAmp=true;});return true;});}await(0,_writeBuildId.writeBuildId)(distDir,buildId);const finalPrerenderRoutes={};const tbdPrerenderRoutes=[];if(staticPages.size>0||ssgPages.size>0||useStatic404){const combinedPages=[...staticPages,...ssgPages];const exportApp=require('../export').default;const exportOptions={silent:true,buildExport:true,threads:config.experimental.cpus,pages:combinedPages,outdir:_path.default.join(distDir,'export')};const exportConfig={...config,initialPageRevalidationMap:{},// Default map will be the collection of automatic statically exported -// pages and SPR pages. -// n.b. we cannot handle this above in combinedPages because the dynamic -// page must be in the `pages` array, but not in the mapping. -exportPathMap:defaultMap=>{// Dynamically routed pages should be prerendered to be used as -// a client-side skeleton (fallback) while data is being fetched. -// This ensures the end-user never sees a 500 or slow response from the -// server. -// -// Note: prerendering disables automatic static optimization. -ssgPages.forEach(page=>{if((0,_utils.isDynamicRoute)(page)){tbdPrerenderRoutes.push(page);if(ssgFallbackPages.has(page)){// Override the rendering for the dynamic page to be treated as a -// fallback render. -defaultMap[page]={page,query:{__nextFallback:true}};}else{// Remove dynamically routed pages from the default path map when -// fallback behavior is disabled. -delete defaultMap[page];}}});// Append the "well-known" routes we should prerender for, e.g. blog -// post slugs. -additionalSsgPaths.forEach((routes,page)=>{routes.forEach(route=>{defaultMap[route]={page};});});if(useStatic404){defaultMap['/404']={page:hasPages404?'/404':'/_error'};}return defaultMap;},exportTrailingSlash:false};await exportApp(destDir||dir,exportOptions,exportConfig);// remove server bundles that were exported -for(const page of staticPages){const{serverBundle}=pageInfos.get(page);await _fs.promises.unlink(serverBundle);}const moveExportedPage=async(page,file,isSsg,ext)=>{file=`${file}.${ext}`;const orig=_path.default.join(exportOptions.outdir,file);const relativeDest=(isLikeServerless?_path.default.join('pages',file):_path.default.join('static',buildId,'pages',file)).replace(/\\/g,'/');const dest=_path.default.join(distDir,isLikeServerless?_constants2.SERVERLESS_DIRECTORY:_constants2.SERVER_DIRECTORY,relativeDest);if(!isSsg){pagesManifest[page]=relativeDest;if(page==='/')pagesManifest['/index']=relativeDest;if(page==='/.amp')pagesManifest['/index.amp']=relativeDest;}await _fs.promises.mkdir(_path.default.dirname(dest),{recursive:true});await _fs.promises.rename(orig,dest);};// Only move /404 to /404 when there is no custom 404 as in that case we don't know about the 404 page -if(!hasPages404&&useStatic404){await moveExportedPage('/404','/404',false,'html');}for(const page of combinedPages){const isSsg=ssgPages.has(page);const isSsgFallback=ssgFallbackPages.has(page);const isDynamic=(0,_utils.isDynamicRoute)(page);const hasAmp=hybridAmpPages.has(page);let file=(0,_normalizePagePath.normalizePagePath)(page);// The dynamic version of SSG pages are only prerendered if the fallback -// is enabled. Below, we handle the specific prerenders of these. -if(!(isSsg&&isDynamic&&!isSsgFallback)){await moveExportedPage(page,file,isSsg,'html');}if(hasAmp&&(!isSsg||isSsg&&!isDynamic)){await moveExportedPage(`${page}.amp`,`${file}.amp`,isSsg,'html');if(isSsg){await moveExportedPage(`${page}.amp`,`${file}.amp`,isSsg,'json');}}if(isSsg){// For a non-dynamic SSG page, we must copy its data file from export. -if(!isDynamic){await moveExportedPage(page,file,true,'json');finalPrerenderRoutes[page]={initialRevalidateSeconds:exportConfig.initialPageRevalidationMap[page],srcRoute:null,dataRoute:_path.default.posix.join('/_next/data',buildId,`${file}.json`)};}else{// For a dynamic SSG page, we did not copy its data exports and only -// copy the fallback HTML file (if present). -// We must also copy specific versions of this page as defined by -// `getStaticPaths` (additionalSsgPaths). -const extraRoutes=additionalSsgPaths.get(page)||[];for(const route of extraRoutes){await moveExportedPage(route,route,true,'html');await moveExportedPage(route,route,true,'json');if(hasAmp){await moveExportedPage(`${route}.amp`,`${route}.amp`,true,'html');await moveExportedPage(`${route}.amp`,`${route}.amp`,true,'json');}finalPrerenderRoutes[route]={initialRevalidateSeconds:exportConfig.initialPageRevalidationMap[route],srcRoute:page,dataRoute:_path.default.posix.join('/_next/data',buildId,`${(0,_normalizePagePath.normalizePagePath)(route)}.json`)};}}}}// remove temporary export folder -await(0,_recursiveDelete.recursiveDelete)(exportOptions.outdir);await _fs.promises.rmdir(exportOptions.outdir);await _fs.promises.writeFile(manifestPath,JSON.stringify(pagesManifest),'utf8');}if(postBuildSpinner)postBuildSpinner.stopAndPersist();console.log();const analysisEnd=process.hrtime(analysisBegin);telemetry.record((0,_events.eventBuildOptimize)(pagePaths,{durationInSeconds:analysisEnd[0],staticPageCount:staticPages.size,staticPropsPageCount:ssgPages.size,serverPropsPageCount:serverPropsPages.size,ssrPageCount:pagePaths.length-(staticPages.size+ssgPages.size+serverPropsPages.size),hasStatic404:useStatic404,hasReportWebVitals:(_namedExports$include=(_namedExports=namedExports)===null||_namedExports===void 0?void 0:_namedExports.includes('reportWebVitals'))!==null&&_namedExports$include!==void 0?_namedExports$include:false}));if(ssgPages.size>0){const finalDynamicRoutes={};tbdPrerenderRoutes.forEach(tbdRoute=>{const normalizedRoute=(0,_normalizePagePath.normalizePagePath)(tbdRoute);const dataRoute=_path.default.posix.join('/_next/data',buildId,`${normalizedRoute}.json`);finalDynamicRoutes[tbdRoute]={routeRegex:(0,_utils.getRouteRegex)(tbdRoute).re.source,dataRoute,fallback:ssgFallbackPages.has(tbdRoute)?`${normalizedRoute}.html`:false,dataRouteRegex:(0,_utils.getRouteRegex)(dataRoute.replace(/\.json$/,'')).re.source.replace(/\(\?:\\\/\)\?\$$/,'\\.json$')};});const prerenderManifest={version:2,routes:finalPrerenderRoutes,dynamicRoutes:finalDynamicRoutes,preview:previewProps};await _fs.promises.writeFile(_path.default.join(distDir,_constants2.PRERENDER_MANIFEST),JSON.stringify(prerenderManifest),'utf8');await generateClientSsgManifest(prerenderManifest,{distDir,buildId,isModern:!!config.experimental.modern});}else{const prerenderManifest={version:2,routes:{},dynamicRoutes:{},preview:previewProps};await _fs.promises.writeFile(_path.default.join(distDir,_constants2.PRERENDER_MANIFEST),JSON.stringify(prerenderManifest),'utf8');// No need to call this fn as we already emitted a default SSG manifest: -// await generateClientSsgManifest(prerenderManifest, { distDir, buildId }) -}await _fs.promises.writeFile(_path.default.join(distDir,_constants2.EXPORT_MARKER),JSON.stringify({version:1,hasExportPathMap:typeof config.exportPathMap==='function',exportTrailingSlash:config.exportTrailingSlash===true}),'utf8');await _fs.promises.unlink(_path.default.join(distDir,_constants2.EXPORT_DETAIL)).catch(err=>{if(err.code==='ENOENT'){return Promise.resolve();}return Promise.reject(err);});staticPages.forEach(pg=>allStaticPages.add(pg));pageInfos.forEach((info,key)=>{allPageInfos.set(key,info);});await(0,_utils2.printTreeView)(Object.keys(mappedPages),allPageInfos,isLikeServerless,{distPath:distDir,buildId:buildId,pagesDir,useStatic404,pageExtensions:config.pageExtensions,buildManifest,isModern:config.experimental.modern});(0,_utils2.printCustomRoutes)({redirects,rewrites,headers});if(tracer){const parsedResults=await tracer.profiler.stopProfiling();await new Promise(resolve=>{if(parsedResults===undefined){tracer.profiler.destroy();tracer.trace.flush();tracer.end(resolve);return;}const cpuStartTime=parsedResults.profile.startTime;const cpuEndTime=parsedResults.profile.endTime;tracer.trace.completeEvent({name:'TaskQueueManager::ProcessTaskFromWorkQueue',id:++tracer.counter,cat:['toplevel'],ts:cpuStartTime,args:{src_file:'../../ipc/ipc_moji_bootstrap.cc',src_func:'Accept'}});tracer.trace.completeEvent({name:'EvaluateScript',id:++tracer.counter,cat:['devtools.timeline'],ts:cpuStartTime,dur:cpuEndTime-cpuStartTime,args:{data:{url:'webpack',lineNumber:1,columnNumber:1,frame:'0xFFF'}}});tracer.trace.instantEvent({name:'CpuProfile',id:++tracer.counter,cat:['disabled-by-default-devtools.timeline'],ts:cpuEndTime,args:{data:{cpuProfile:parsedResults.profile}}});tracer.profiler.destroy();tracer.trace.flush();tracer.end(resolve);});}await telemetry.flush();}function generateClientSsgManifest(prerenderManifest,{buildId,distDir,isModern}){const ssgPages=new Set([...Object.entries(prerenderManifest.routes)// Filter out dynamic routes -.filter(([,{srcRoute}])=>srcRoute==null).map(([route])=>route),...Object.keys(prerenderManifest.dynamicRoutes)]);const clientSsgManifestPaths=['_ssgManifest.js',isModern&&'_ssgManifest.module.js'].filter(Boolean).map(f=>_path.default.join(`${_constants2.CLIENT_STATIC_FILES_PATH}/${buildId}`,f));const clientSsgManifestContent=`self.__SSG_MANIFEST=${(0,_devalue.default)(ssgPages)};self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()`;clientSsgManifestPaths.forEach(clientSsgManifestPath=>(0,_fs.writeFileSync)(_path.default.join(distDir,clientSsgManifestPath),clientSsgManifestContent));} -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-build.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-build.js deleted file mode 100644 index ff69015f39..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-build.js +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env node -"use strict";exports.__esModule=true;exports.nextBuild=void 0;var _fs=require("fs");var _index=_interopRequireDefault(require("next/dist/compiled/arg/index.js"));var _path=require("path");var _build=_interopRequireDefault(require("../build"));var _utils=require("../server/lib/utils");var _findPagesDir=require("../lib/find-pages-dir");function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}const nextBuild=argv=>{const args=(0,_index.default)({// Types -'--help':Boolean,'--pagesDir':String,// Aliases -'-h':'--help'},{argv});if(args['--help']){(0,_utils.printAndExit)(` - Description - Compiles the application for production deployment - - Options - --pagesDir Location of pages dir - - Usage - $ next build - - represents the directory of the Next.js application. - If no directory is provided, the current directory will be used. - `,0);}const dir=(0,_path.resolve)('.');const destDir=(0,_path.resolve)(args._[0]||'.');if(args['--pagesDir'])(0,_findPagesDir.setPagesDir)((0,_path.join)(dir,args['--pagesDir']));// Check if the provided directory exists -if(!(0,_fs.existsSync)(dir)){(0,_utils.printAndExit)(`> No such directory exists as the project root: ${dir}`);}(0,_build.default)(dir,null,destDir).then(()=>process.exit(0)).catch(err=>{console.error('');console.error('> Build error occurred');(0,_utils.printAndExit)(err);});};exports.nextBuild=nextBuild; -//# sourceMappingURL=next-build.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-dev.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-dev.js deleted file mode 100644 index acf9c53c03..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-dev.js +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env node -"use strict";exports.__esModule=true;exports.nextDev=void 0;var _path=require("path");var _index=_interopRequireDefault(require("next/dist/compiled/arg/index.js"));var _fs=require("fs");var _startServer=_interopRequireDefault(require("../server/lib/start-server"));var _utils=require("../server/lib/utils");var _output=require("../build/output");var _findPagesDir=require("../lib/find-pages-dir");function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}const nextDev=argv=>{const args=(0,_index.default)({// Types -'--help':Boolean,'--port':Number,'--hostname':String,'--pagesDir':String,// Aliases -'-h':'--help','-p':'--port','-H':'--hostname'},{argv});if(args['--help']){// tslint:disable-next-line -console.log(` - Description - Starts the application in development mode (hot-code reloading, error - reporting, etc) - - Usage - $ next dev -p - - represents the directory of the Next.js application. - If no directory is provided, the current directory will be used. - - Options - --port, -p A port number on which to start the application - --hostname, -H Hostname on which to start the application - --help, -h Displays this message - --pagesDir Location of pages dir - `);process.exit(0);}const dir=(0,_path.resolve)('.');if(args['--pagesDir'])(0,_findPagesDir.setPagesDir)(_path.join(dir,args['--pagesDir']));// Check if pages dir exists and warn if not -if(!(0,_fs.existsSync)(dir)){(0,_utils.printAndExit)(`> No such directory exists as the project root: ${dir}`);}const port=args['--port']||3000;const appUrl=`http://${args['--hostname']||'localhost'}:${port}`;(0,_output.startedDevelopmentServer)(appUrl);(0,_startServer.default)({dir,dev:true,isNextDevCommand:true},port,args['--hostname']).then(async app=>{await app.prepare();}).catch(err=>{if(err.code==='EADDRINUSE'){let errorMessage=`Port ${port} is already in use.`;const pkgAppPath=require('next/dist/compiled/find-up').sync('package.json',{cwd:dir});const appPackage=require(pkgAppPath);if(appPackage.scripts){const nextScript=Object.entries(appPackage.scripts).find(scriptLine=>scriptLine[1]==='next');if(nextScript){errorMessage+=`\nUse \`npm run ${nextScript[0]} -- -p \`.`;}}// tslint:disable-next-line -console.error(errorMessage);}else{// tslint:disable-next-line -console.error(err);}process.nextTick(()=>process.exit(1));});};exports.nextDev=nextDev; -//# sourceMappingURL=next-dev.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-export.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-export.js deleted file mode 100644 index 63864114ea..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/cli/next-export.js +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env node -"use strict";exports.__esModule=true;exports.nextExport=void 0;var _path=require("path");var _fs=require("fs");var _index=_interopRequireDefault(require("next/dist/compiled/arg/index.js"));var _export=_interopRequireDefault(require("../export"));var _utils=require("../server/lib/utils");var _findPagesDir=require("../lib/find-pages-dir");function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}const nextExport=argv=>{const args=(0,_index.default)({// Types -'--help':Boolean,'--silent':Boolean,'--outdir':String,'--threads':Number,'--pagesDir':String,// Aliases -'-h':'--help','-s':'--silent','-o':'--outdir'},{argv});if(args['--help']){// tslint:disable-next-line -console.log(` - Description - Exports the application for production deployment - - Usage - $ next export [options] - - represents the directory of the Next.js application. - If no directory is provided, the current directory will be used. - - Options - -h - list this help - -o - set the output dir (defaults to 'out') - -s - do not print any messages to console - --pagesDir Location of pages dir - `);process.exit(0);}const dir=(0,_path.resolve)('.');const destDir=(0,_path.resolve)(args._[0]||'.');if(args['--pagesDir'])(0,_findPagesDir.setPagesDir)((0,_path.join)(dir,args['--pagesDir']));// Check if pages dir exists and warn if not -if(!(0,_fs.existsSync)(dir)){(0,_utils.printAndExit)(`> No such directory exists as the project root: ${dir}`);}const options={silent:args['--silent']||false,threads:args['--threads'],outdir:args['--outdir']?(0,_path.resolve)(args['--outdir']):(0,_path.join)(dir,'out')};(0,_export.default)(dir,options,null,destDir).then(()=>{(0,_utils.printAndExit)('Export successful',0);}).catch(err=>{(0,_utils.printAndExit)(err);});};exports.nextExport=nextExport; -//# sourceMappingURL=next-export.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/export/index.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/export/index.js deleted file mode 100644 index 11972bf29b..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/export/index.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict";exports.__esModule=true;exports.default=exportApp;var _chalk=_interopRequireDefault(require("next/dist/compiled/chalk"));var _findUp=_interopRequireDefault(require("next/dist/compiled/find-up"));var _fs=require("fs");var _jestWorker=_interopRequireDefault(require("jest-worker"));var _os=require("os");var _path=require("path");var _util=require("util");var _index=require("../build/output/index");var _spinner=_interopRequireDefault(require("../build/spinner"));var _constants=require("../lib/constants");var _recursiveCopy=require("../lib/recursive-copy");var _recursiveDelete=require("../lib/recursive-delete");var _constants2=require("../next-server/lib/constants");var _config=_interopRequireWildcard(require("../next-server/server/config"));var _events=require("../telemetry/events");var _storage=require("../telemetry/storage");var _normalizePagePath=require("../next-server/server/normalize-page-path");var _loadEnvConfig=require("../lib/load-env-config");function _getRequireWildcardCache(){if(typeof WeakMap!=="function")return null;var cache=new WeakMap();_getRequireWildcardCache=function(){return cache;};return cache;}function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj;}if(obj===null||typeof obj!=="object"&&typeof obj!=="function"){return{default:obj};}var cache=_getRequireWildcardCache();if(cache&&cache.has(obj)){return cache.get(obj);}var newObj={};var hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;if(desc&&(desc.get||desc.set)){Object.defineProperty(newObj,key,desc);}else{newObj[key]=obj[key];}}}newObj.default=obj;if(cache){cache.set(obj,newObj);}return newObj;}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}const exists=(0,_util.promisify)(_fs.exists);const createProgress=(total,label='Exporting')=>{let curProgress=0;let progressSpinner=(0,_spinner.default)(`${label} (${curProgress}/${total})`,{spinner:{frames:['[ ]','[= ]','[== ]','[=== ]','[ ===]','[ ==]','[ =]','[ ]','[ =]','[ ==]','[ ===]','[====]','[=== ]','[== ]','[= ]'],interval:80}});return()=>{curProgress++;const newText=`${label} (${curProgress}/${total})`;if(progressSpinner){progressSpinner.text=newText;}else{console.log(newText);}if(curProgress===total&&progressSpinner){progressSpinner.stop();console.log(newText);}};};async function exportApp(dir,options,configuration,destDir){var _nextConfig$amp,_nextConfig$experimen,_nextConfig$experimen2,_nextConfig$experimen3;function log(message){if(options.silent){return;}console.log(message);}dir=(0,_path.resolve)(dir);// attempt to load global env values so they are available in next.config.js -(0,_loadEnvConfig.loadEnvConfig)(dir);const nextConfig=configuration||(0,_config.default)(_constants2.PHASE_EXPORT,dir);const threads=options.threads||Math.max((0,_os.cpus)().length-1,1);const distDir=(0,_path.join)(destDir||dir,nextConfig.distDir);const telemetry=options.buildExport?null:new _storage.Telemetry({distDir});if(telemetry){telemetry.record((0,_events.eventCliSession)(_constants2.PHASE_EXPORT,distDir,{cliCommand:'export',isSrcDir:null,hasNowJson:!!(await(0,_findUp.default)('now.json',{cwd:dir})),isCustomServer:null}));}const subFolders=nextConfig.exportTrailingSlash;const isLikeServerless=nextConfig.target!=='server';log(`> using build directory: ${distDir}`);if(!(0,_fs.existsSync)(distDir)){throw new Error(`Build directory ${distDir} does not exist. Make sure you run "next build" before running "next start" or "next export".`);}const buildId=(0,_fs.readFileSync)((0,_path.join)(distDir,_constants2.BUILD_ID_FILE),'utf8');const pagesManifest=!options.pages&&require((0,_path.join)(distDir,isLikeServerless?_constants2.SERVERLESS_DIRECTORY:_constants2.SERVER_DIRECTORY,_constants2.PAGES_MANIFEST));let prerenderManifest=undefined;try{prerenderManifest=require((0,_path.join)(distDir,_constants2.PRERENDER_MANIFEST));}catch(_){}const distPagesDir=(0,_path.join)(distDir,isLikeServerless?_constants2.SERVERLESS_DIRECTORY:(0,_path.join)(_constants2.SERVER_DIRECTORY,'static',buildId),'pages');const excludedPrerenderRoutes=new Set();const pages=options.pages||Object.keys(pagesManifest);const defaultPathMap={};let hasApiRoutes=false;for(const page of pages){var _prerenderManifest;// _document and _app are not real pages -// _error is exported as 404.html later on -// API Routes are Node.js functions -if(page.match(_constants.API_ROUTE)){hasApiRoutes=true;continue;}if(page==='/_document'||page==='/_app'||page==='/_error'){continue;}// iSSG pages that are dynamic should not export templated version by -// default. In most cases, this would never work. There is no server that -// could run `getStaticProps`. If users make their page work lazily, they -// can manually add it to the `exportPathMap`. -if((_prerenderManifest=prerenderManifest)===null||_prerenderManifest===void 0?void 0:_prerenderManifest.dynamicRoutes[page]){excludedPrerenderRoutes.add(page);continue;}defaultPathMap[page]={page};}// Initialize the output directory -const outDir=destDir?(0,_path.join)(destDir,'out'):options.outdir;if(outDir===(0,_path.join)(dir,'public')){throw new Error(`The 'public' directory is reserved in Next.js and can not be used as the export out directory. https://err.sh/vercel/next.js/can-not-output-to-public`);}await(0,_recursiveDelete.recursiveDelete)((0,_path.join)(outDir));await _fs.promises.mkdir((0,_path.join)(outDir,'_next',buildId),{recursive:true});(0,_fs.writeFileSync)((0,_path.join)(distDir,_constants2.EXPORT_DETAIL),JSON.stringify({version:1,outDirectory:outDir,success:false}),'utf8');// Copy static directory -if(!options.buildExport&&(0,_fs.existsSync)((0,_path.join)(dir,'static'))){log(' copying "static" directory');await(0,_recursiveCopy.recursiveCopy)((0,_path.join)(dir,'static'),(0,_path.join)(outDir,'static'));}// Copy .next/static directory -if((0,_fs.existsSync)((0,_path.join)(distDir,_constants2.CLIENT_STATIC_FILES_PATH))){log(' copying "static build" directory');await(0,_recursiveCopy.recursiveCopy)((0,_path.join)(distDir,_constants2.CLIENT_STATIC_FILES_PATH),(0,_path.join)(outDir,'_next',_constants2.CLIENT_STATIC_FILES_PATH));}// Get the exportPathMap from the config file -if(typeof nextConfig.exportPathMap!=='function'){console.log(`> No "exportPathMap" found in "${_constants2.CONFIG_FILE}". Generating map from "./pages"`);nextConfig.exportPathMap=async defaultMap=>{return defaultMap;};}// Start the rendering process -const renderOpts={dir,buildId,nextExport:true,assetPrefix:nextConfig.assetPrefix.replace(/\/$/,''),distDir,dev:false,staticMarkup:false,hotReloader:null,basePath:nextConfig.experimental.basePath,canonicalBase:((_nextConfig$amp=nextConfig.amp)===null||_nextConfig$amp===void 0?void 0:_nextConfig$amp.canonicalBase)||'',isModern:nextConfig.experimental.modern,ampValidatorPath:((_nextConfig$experimen=nextConfig.experimental.amp)===null||_nextConfig$experimen===void 0?void 0:_nextConfig$experimen.validator)||undefined,ampSkipValidation:((_nextConfig$experimen2=nextConfig.experimental.amp)===null||_nextConfig$experimen2===void 0?void 0:_nextConfig$experimen2.skipValidation)||false,ampOptimizerConfig:((_nextConfig$experimen3=nextConfig.experimental.amp)===null||_nextConfig$experimen3===void 0?void 0:_nextConfig$experimen3.optimizer)||undefined};const{serverRuntimeConfig,publicRuntimeConfig}=nextConfig;if(Object.keys(publicRuntimeConfig).length>0){;renderOpts.runtimeConfig=publicRuntimeConfig;}// We need this for server rendering the Link component. -;global.__NEXT_DATA__={nextExport:true};log(` launching ${threads} workers`);const exportPathMap=await nextConfig.exportPathMap(defaultPathMap,{dev:false,dir,outDir,distDir,buildId});if(!exportPathMap['/404']&&!exportPathMap['/404.html']){exportPathMap['/404']=exportPathMap['/404.html']={page:'/_error'};}// make sure to prevent duplicates -const exportPaths=[...new Set(Object.keys(exportPathMap).map(path=>(0,_normalizePagePath.normalizePagePath)(path).replace(/^\/index$/,'')||'/'))];const filteredPaths=exportPaths.filter(// Remove API routes -route=>!exportPathMap[route].page.match(_constants.API_ROUTE));if(filteredPaths.length!==exportPaths.length){hasApiRoutes=true;}if(prerenderManifest&&!options.buildExport){const fallbackTruePages=new Set();for(const key of Object.keys(prerenderManifest.dynamicRoutes)){// only error if page is included in path map -if(!exportPathMap[key]&&!excludedPrerenderRoutes.has(key)){continue;}if(prerenderManifest.dynamicRoutes[key].fallback!==false){fallbackTruePages.add(key);}}if(fallbackTruePages.size){throw new Error(`Found pages with \`fallback: true\`:\n${[...fallbackTruePages].join('\n')}\n${_constants.SSG_FALLBACK_EXPORT_ERROR}\n`);}}// Warn if the user defines a path for an API page -if(hasApiRoutes){log(_chalk.default.bold.red(`Attention`)+': '+_chalk.default.yellow(`Statically exporting a Next.js application via \`next export\` disables API routes.`)+`\n`+_chalk.default.yellow(`This command is meant for static-only hosts, and is`+' '+_chalk.default.bold(`not necessary to make your application static.`))+`\n`+_chalk.default.yellow(`Pages in your application without server-side data dependencies will be automatically statically exported by \`next build\`, including pages powered by \`getStaticProps\`.`)+`\nLearn more: https://err.sh/vercel/next.js/api-routes-static-export`);}const progress=!options.silent&&createProgress(filteredPaths.length);const pagesDataDir=options.buildExport?outDir:(0,_path.join)(outDir,'_next/data',buildId);const ampValidations={};let hadValidationError=false;const publicDir=(0,_path.join)(dir,_constants2.CLIENT_PUBLIC_FILES_PATH);// Copy public directory -if(!options.buildExport&&(0,_fs.existsSync)(publicDir)){log(' copying "public" directory');await(0,_recursiveCopy.recursiveCopy)(publicDir,outDir,{filter(path){// Exclude paths used by pages -return!exportPathMap[path];}});}const worker=new _jestWorker.default(require.resolve('./worker'),{maxRetries:0,numWorkers:threads,enableWorkerThreads:nextConfig.experimental.workerThreads,exposedMethods:['default']});worker.getStdout().pipe(process.stdout);worker.getStderr().pipe(process.stderr);let renderError=false;const errorPaths=[];await Promise.all(filteredPaths.map(async path=>{const result=await worker.default({path,pathMap:exportPathMap[path],distDir,buildId,outDir,pagesDataDir,renderOpts,serverRuntimeConfig,subFolders,buildExport:options.buildExport,serverless:(0,_config.isTargetLikeServerless)(nextConfig.target)});for(const validation of result.ampValidations||[]){const{page,result}=validation;ampValidations[page]=result;hadValidationError=hadValidationError||Array.isArray(result===null||result===void 0?void 0:result.errors)&&result.errors.length>0;}renderError=renderError||!!result.error;if(!!result.error)errorPaths.push(path);if(options.buildExport&&typeof result.fromBuildExportRevalidate!=='undefined'){configuration.initialPageRevalidationMap[path]=result.fromBuildExportRevalidate;}if(progress)progress();}));worker.end();// copy prerendered routes to outDir -if(!options.buildExport&&prerenderManifest){await Promise.all(Object.keys(prerenderManifest.routes).map(async route=>{route=(0,_normalizePagePath.normalizePagePath)(route);const orig=(0,_path.join)(distPagesDir,route);const htmlDest=(0,_path.join)(outDir,`${route}${subFolders&&route!=='/index'?`${_path.sep}index`:''}.html`);const ampHtmlDest=(0,_path.join)(outDir,`${route}.amp${subFolders?`${_path.sep}index`:''}.html`);const jsonDest=(0,_path.join)(pagesDataDir,`${route}.json`);await _fs.promises.mkdir((0,_path.dirname)(htmlDest),{recursive:true});await _fs.promises.mkdir((0,_path.dirname)(jsonDest),{recursive:true});await _fs.promises.copyFile(`${orig}.html`,htmlDest);await _fs.promises.copyFile(`${orig}.json`,jsonDest);if(await exists(`${orig}.amp.html`)){await _fs.promises.mkdir((0,_path.dirname)(ampHtmlDest),{recursive:true});await _fs.promises.copyFile(`${orig}.amp.html`,ampHtmlDest);}}));}if(Object.keys(ampValidations).length){console.log((0,_index.formatAmpMessages)(ampValidations));}if(hadValidationError){throw new Error(`AMP Validation caused the export to fail. https://err.sh/vercel/next.js/amp-export-validation`);}if(renderError){throw new Error(`Export encountered errors on following paths:\n\t${errorPaths.sort().join('\n\t')}`);}// Add an empty line to the console for the better readability. -log('');(0,_fs.writeFileSync)((0,_path.join)(distDir,_constants2.EXPORT_DETAIL),JSON.stringify({version:1,outDirectory:outDir,success:true}),'utf8');if(telemetry){await telemetry.flush();}} -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/lib/find-pages-dir.js b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/lib/find-pages-dir.js deleted file mode 100644 index e744468658..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/lib/find-pages-dir.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict";exports.__esModule=true;exports.setPagesDir=setPagesDir;exports.findPagesDir=findPagesDir;exports.existsSync=void 0;var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}let pagesDir=null;const existsSync=f=>{try{_fs.default.accessSync(f,_fs.default.constants.F_OK);return true;}catch(_){return false;}};exports.existsSync=existsSync;function setPagesDir(dir){pagesDir=dir;console.log('set dir',pagesDir);}function findPagesDir(dir){if(pagesDir)return pagesDir;// prioritize ./pages over ./src/pages -let curDir=_path.default.join(dir,'pages');if(existsSync(curDir))return curDir;curDir=_path.default.join(dir,'src/pages');if(existsSync(curDir))return curDir;// Check one level up the tree to see if the pages directory might be there -if(existsSync(_path.default.join(dir,'..','pages'))){throw new Error('> No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?');}throw new Error("> Couldn't find a `pages` directory. Please create one under the project root");} -//# sourceMappingURL=find-pages-dir.js.map \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/next.patch b/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/next.patch deleted file mode 100644 index 6782eb2090..0000000000 --- a/packages/rnv/pluginTemplates/next/overrides@9.4.3/dist/next.patch +++ /dev/null @@ -1,273 +0,0 @@ -diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts -index 1cd104772..f9f46b7bf 100644 ---- a/packages/next/build/entries.ts -+++ b/packages/next/build/entries.ts -@@ -49,6 +49,10 @@ export function createPagesMapping( - pages['/_error'] = pages['/_error'] || 'next/dist/pages/_error' - pages['/_document'] = pages['/_document'] || 'next/dist/pages/_document' - -+ Object.keys(pages).forEach(key => { -+ if (key.includes('.')) delete pages[key] -+ }) -+ - return pages - } - -diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts -index 2ef7065c8..7c3708e92 100644 ---- a/packages/next/build/index.ts -+++ b/packages/next/build/index.ts -@@ -98,7 +98,7 @@ export type PrerenderManifest = { - preview: __ApiPreviewProps - } - --export default async function build(dir: string, conf = null): Promise { -+export default async function build(dir: string, conf = null, destDir): Promise { - if (!(await isWriteable(dir))) { - throw new Error( - '> Build directory is not writeable. https://err.sh/vercel/next.js/build-dir-not-writeable' -@@ -111,7 +111,7 @@ export default async function build(dir: string, conf = null): Promise { - const config = loadConfig(PHASE_PRODUCTION_BUILD, dir, conf) - const { target } = config - const buildId = await generateBuildId(config.generateBuildId, nanoid) -- const distDir = path.join(dir, config.distDir) -+ const distDir = path.join(destDir, config.distDir) - const headers: Header[] = [] - const rewrites: Rewrite[] = [] - const redirects: Redirect[] = [] -@@ -196,7 +196,7 @@ export default async function build(dir: string, conf = null): Promise { - - const pagePaths: string[] = await collectPages( - pagesDir, -- config.pageExtensions -+ config.pageExtensionsRnv || config.pageExtensions - ) - - // needed for static exporting since we want to replace with HTML -@@ -347,7 +347,7 @@ export default async function build(dir: string, conf = null): Promise { - ) - - const configs = await Promise.all([ -- getBaseWebpackConfig(dir, { -+ getBaseWebpackConfig(destDir || dir, { - tracer, - buildId, - isServer: false, -@@ -356,7 +356,7 @@ export default async function build(dir: string, conf = null): Promise { - pagesDir, - entrypoints: entrypoints.client, - }), -- getBaseWebpackConfig(dir, { -+ getBaseWebpackConfig(destDir || dir, { - tracer, - buildId, - isServer: true, -@@ -822,7 +822,7 @@ export default async function build(dir: string, conf = null): Promise { - exportTrailingSlash: false, - } - -- await exportApp(dir, exportOptions, exportConfig) -+ await exportApp(destDir || dir, exportOptions, exportConfig) - - // remove server bundles that were exported - for (const page of staticPages) { -diff --git a/packages/next/cli/next-build.ts b/packages/next/cli/next-build.ts -index 21bf0bfd7..7d7a67c24 100755 ---- a/packages/next/cli/next-build.ts -+++ b/packages/next/cli/next-build.ts -@@ -1,17 +1,19 @@ - #!/usr/bin/env node - import { existsSync } from 'fs' - import arg from 'next/dist/compiled/arg/index.js' --import { resolve } from 'path' -+import { resolve, join } from 'path' - - import { cliCommand } from '../bin/next' - import build from '../build' - import { printAndExit } from '../server/lib/utils' -+import { setPagesDir } from '../lib/find-pages-dir' - - const nextBuild: cliCommand = (argv) => { - const args = arg( - { - // Types - '--help': Boolean, -+ '--pagesDir': String, - // Aliases - '-h': '--help', - }, -@@ -24,6 +26,9 @@ const nextBuild: cliCommand = (argv) => { - Description - Compiles the application for production deployment - -+ Options -+ --pagesDir Location of pages dir -+ - Usage - $ next build - -@@ -34,14 +39,17 @@ const nextBuild: cliCommand = (argv) => { - ) - } - -- const dir = resolve(args._[0] || '.') -+ const dir = resolve('.'); -+ const destDir = resolve(args._[0] || '.') -+ -+ if (args['--pagesDir']) setPagesDir(join(dir, args['--pagesDir'])) - - // Check if the provided directory exists - if (!existsSync(dir)) { - printAndExit(`> No such directory exists as the project root: ${dir}`) - } - -- build(dir) -+ build(dir, null, destDir) - .then(() => process.exit(0)) - .catch((err) => { - console.error('') -diff --git a/packages/next/cli/next-dev.ts b/packages/next/cli/next-dev.ts -index e010277ff..a65caca0e 100755 ---- a/packages/next/cli/next-dev.ts -+++ b/packages/next/cli/next-dev.ts -@@ -1,11 +1,12 @@ - #!/usr/bin/env node --import { resolve } from 'path' -+import { resolve, join } from 'path' - import arg from 'next/dist/compiled/arg/index.js' - import { existsSync } from 'fs' - import startServer from '../server/lib/start-server' - import { printAndExit } from '../server/lib/utils' - import { startedDevelopmentServer } from '../build/output' - import { cliCommand } from '../bin/next' -+import { setPagesDir } from '../lib/find-pages-dir' - - const nextDev: cliCommand = (argv) => { - const args = arg( -@@ -14,6 +15,7 @@ const nextDev: cliCommand = (argv) => { - '--help': Boolean, - '--port': Number, - '--hostname': String, -+ '--pagesDir': String, - - // Aliases - '-h': '--help', -@@ -40,11 +42,13 @@ const nextDev: cliCommand = (argv) => { - --port, -p A port number on which to start the application - --hostname, -H Hostname on which to start the application - --help, -h Displays this message -+ --pagesDir Location of pages dir - `) - process.exit(0) - } - -- const dir = resolve(args._[0] || '.') -+ const dir = resolve('.') -+ if (args['--pagesDir']) setPagesDir(join(dir, args['--pagesDir'])) - - // Check if pages dir exists and warn if not - if (!existsSync(dir)) { -diff --git a/packages/next/cli/next-export.ts b/packages/next/cli/next-export.ts -index 29e8085d0..1aaaa0649 100755 ---- a/packages/next/cli/next-export.ts -+++ b/packages/next/cli/next-export.ts -@@ -5,6 +5,7 @@ import arg from 'next/dist/compiled/arg/index.js' - import exportApp from '../export' - import { printAndExit } from '../server/lib/utils' - import { cliCommand } from '../bin/next' -+import { setPagesDir } from '../lib/find-pages-dir' - - const nextExport: cliCommand = (argv) => { - const args = arg( -@@ -14,6 +15,7 @@ const nextExport: cliCommand = (argv) => { - '--silent': Boolean, - '--outdir': String, - '--threads': Number, -+ '--pagesDir': String, - - // Aliases - '-h': '--help', -@@ -39,11 +41,14 @@ const nextExport: cliCommand = (argv) => { - -h - list this help - -o - set the output dir (defaults to 'out') - -s - do not print any messages to console -+ --pagesDir Location of pages dir - `) - process.exit(0) - } - -- const dir = resolve(args._[0] || '.') -+ const dir = resolve('.') -+ const destDir = resolve(args._[0] || '.') -+ if (args['--pagesDir']) setPagesDir(join(dir, args['--pagesDir'])) - - // Check if pages dir exists and warn if not - if (!existsSync(dir)) { -@@ -56,7 +61,7 @@ const nextExport: cliCommand = (argv) => { - outdir: args['--outdir'] ? resolve(args['--outdir']) : join(dir, 'out'), - } - -- exportApp(dir, options) -+ exportApp(dir, options, null, destDir) - .then(() => { - printAndExit('Export successful', 0) - }) -diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts -index 5d4bd0da9..e222aad1a 100644 ---- a/packages/next/export/index.ts -+++ b/packages/next/export/index.ts -@@ -98,7 +98,8 @@ interface ExportOptions { - export default async function exportApp( - dir: string, - options: ExportOptions, -- configuration?: any -+ configuration?: any, -+ destDir?: string - ): Promise { - function log(message: string): void { - if (options.silent) { -@@ -114,7 +115,7 @@ export default async function exportApp( - - const nextConfig = configuration || loadConfig(PHASE_EXPORT, dir) - const threads = options.threads || Math.max(cpus().length - 1, 1) -- const distDir = join(dir, nextConfig.distDir) -+ const distDir = join(destDir || dir, nextConfig.distDir) - - const telemetry = options.buildExport ? null : new Telemetry({ distDir }) - -@@ -194,7 +195,7 @@ export default async function exportApp( - } - - // Initialize the output directory -- const outDir = options.outdir -+ const outDir = destDir ? join(destDir, 'out') : options.outdir - - if (outDir === join(dir, 'public')) { - throw new Error( -diff --git a/packages/next/lib/find-pages-dir.ts b/packages/next/lib/find-pages-dir.ts -index a4acde163..7722c7a64 100644 ---- a/packages/next/lib/find-pages-dir.ts -+++ b/packages/next/lib/find-pages-dir.ts -@@ -1,6 +1,8 @@ - import fs from 'fs' - import path from 'path' - -+let pagesDir = null; -+ - export const existsSync = (f: string): boolean => { - try { - fs.accessSync(f, fs.constants.F_OK) -@@ -10,7 +12,13 @@ export const existsSync = (f: string): boolean => { - } - } - -+export function setPagesDir(dir) { -+ pagesDir = dir; -+ console.log('set dir', pagesDir); -+} -+ - export function findPagesDir(dir: string): string { -+ if (pagesDir) return pagesDir; - // prioritize ./pages over ./src/pages - let curDir = path.join(dir, 'pages') - if (existsSync(curDir)) return curDir diff --git a/packages/rnv/pluginTemplates/react-native-cached-image/overrides/CachedImage.js b/packages/rnv/pluginTemplates/react-native-cached-image/CachedImage.js similarity index 100% rename from packages/rnv/pluginTemplates/react-native-cached-image/overrides/CachedImage.js rename to packages/rnv/pluginTemplates/react-native-cached-image/CachedImage.js diff --git a/packages/rnv/pluginTemplates/react-native-cached-image/overrides.json b/packages/rnv/pluginTemplates/react-native-cached-image/overrides.json new file mode 100644 index 0000000000..6f7a914fe8 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-cached-image/overrides.json @@ -0,0 +1,7 @@ +{ + "overrides": { + "utils/fsUtils.js": { + "require('rn-fetch-blob').default": "require('react-native-blob-util').default" + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-cached-image/overrides/utils/fsUtils.js b/packages/rnv/pluginTemplates/react-native-cached-image/utils/fsUtils.js similarity index 100% rename from packages/rnv/pluginTemplates/react-native-cached-image/overrides/utils/fsUtils.js rename to packages/rnv/pluginTemplates/react-native-cached-image/utils/fsUtils.js diff --git a/packages/rnv/pluginTemplates/react-native-cached-image/overrides/utils/pathUtils.js b/packages/rnv/pluginTemplates/react-native-cached-image/utils/pathUtils.js similarity index 100% rename from packages/rnv/pluginTemplates/react-native-cached-image/overrides/utils/pathUtils.js rename to packages/rnv/pluginTemplates/react-native-cached-image/utils/pathUtils.js diff --git a/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/android/build.gradle b/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/android/build.gradle new file mode 100644 index 0000000000..9b95b69255 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/android/build.gradle @@ -0,0 +1,79 @@ +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +buildscript { + // The Android Gradle plugin is only required when opening the android folder stand-alone. + // This avoids unnecessary downloads and potential conflicts when the library is included as a + // module dependency in an application project. + if (project == rootProject) { + repositories { + google() + jcenter() + } + + dependencies { + //noinspection GradleDependency + classpath("com.android.tools.build:gradle:3.6.3") + } + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 28) + buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') + + defaultConfig { + minSdkVersion safeExtGet('minSdkVersion', 16) + targetSdkVersion safeExtGet('targetSdkVersion', 28) + } + + flavorDimensions "react-native-camera" + + productFlavors { + general { + dimension "react-native-camera" + } + mlkit { + dimension "react-native-camera" + } + } + + lintOptions { + abortOnError false + warning 'InvalidPackage' + } + + packagingOptions { + exclude 'META-INF/androidx.exifinterface_exifinterface.version' + exclude 'META-INF/proguard/androidx-annotations.pro' + } +} + +repositories { + google() + jcenter() + mavenCentral() + maven { url "https://jitpack.io" } + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } +} + +dependencies { + def googlePlayServicesVisionVersion = safeExtGet('googlePlayServicesVisionVersion', safeExtGet('googlePlayServicesVersion', '17.0.2')) + + //noinspection GradleDynamicVersion + implementation 'com.facebook.react:react-native:+' // From node_modules + implementation "com.google.zxing:core:3.3.3" + implementation "com.drewnoakes:metadata-extractor:2.16.0" + generalImplementation "com.google.android.gms:play-services-vision:$googlePlayServicesVisionVersion" + implementation "androidx.exifinterface:exifinterface:1.0.0" + implementation "androidx.annotation:annotation:1.0.0" + implementation "androidx.legacy:legacy-support-v4:1.0.0" + mlkitImplementation "com.google.firebase:firebase-ml-vision:${safeExtGet('firebase-ml-vision', '19.0.3')}" + mlkitImplementation "com.google.firebase:firebase-ml-vision-face-model:${safeExtGet('firebase-ml-vision-face-model', '17.0.2')}" +} diff --git a/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/ios/RN/RNCamera.m b/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/ios/RN/RNCamera.m new file mode 100644 index 0000000000..5450081b6d --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-camera/overrides@3.40.0/ios/RN/RNCamera.m @@ -0,0 +1,2263 @@ +#import "RNCamera.h" +#import "RNCameraUtils.h" +#import "RNImageUtils.h" +#import "RNFileSystem.h" +#import +#import +#import +#import +#import +#import "RNSensorOrientationChecker.h" +#import "RNCustomWhiteBalanceSettings.h" + +@interface RNCamera () + +@property (nonatomic, weak) RCTBridge *bridge; +@property (nonatomic,strong) RNSensorOrientationChecker * sensorOrientationChecker; + +@property (nonatomic,strong) UIPinchGestureRecognizer *pinchGestureRecognizer; +@property (nonatomic, strong) RCTPromiseResolveBlock videoRecordedResolve; +@property (nonatomic, strong) RCTPromiseRejectBlock videoRecordedReject; +@property (nonatomic, strong) id textDetector; +@property (nonatomic, strong) id faceDetector; +@property (nonatomic, strong) id barcodeDetector; + +@property (nonatomic, copy) RCTDirectEventBlock onCameraReady; +@property (nonatomic, copy) RCTDirectEventBlock onAudioInterrupted; +@property (nonatomic, copy) RCTDirectEventBlock onAudioConnected; +@property (nonatomic, copy) RCTDirectEventBlock onMountError; +@property (nonatomic, copy) RCTDirectEventBlock onBarCodeRead; +@property (nonatomic, copy) RCTDirectEventBlock onTouch; +@property (nonatomic, copy) RCTDirectEventBlock onTextRecognized; +@property (nonatomic, copy) RCTDirectEventBlock onFacesDetected; +@property (nonatomic, copy) RCTDirectEventBlock onGoogleVisionBarcodesDetected; +@property (nonatomic, copy) RCTDirectEventBlock onPictureTaken; +@property (nonatomic, copy) RCTDirectEventBlock onPictureSaved; +@property (nonatomic, copy) RCTDirectEventBlock onRecordingStart; +@property (nonatomic, copy) RCTDirectEventBlock onRecordingEnd; +@property (nonatomic, assign) BOOL finishedReadingText; +@property (nonatomic, assign) BOOL finishedDetectingFace; +@property (nonatomic, copy) NSDate *startText; +@property (nonatomic, copy) NSDate *startFace; + +@property (nonatomic, copy) RCTDirectEventBlock onSubjectAreaChanged; +@property (nonatomic, assign) BOOL isFocusedOnPoint; +@property (nonatomic, assign) BOOL isExposedOnPoint; +@property (nonatomic, assign) BOOL invertImageData; + +@end + +@implementation RNCamera + +static NSDictionary *defaultFaceDetectorOptions = nil; + +BOOL _recordRequested = NO; +BOOL _sessionInterrupted = NO; + + +- (id)initWithBridge:(RCTBridge *)bridge +{ + if ((self = [super init])) { + self.bridge = bridge; + self.session = [AVCaptureSession new]; + self.sessionQueue = dispatch_queue_create("cameraQueue", DISPATCH_QUEUE_SERIAL); + self.sensorOrientationChecker = [RNSensorOrientationChecker new]; + self.textDetector = [self createTextDetector]; + self.faceDetector = [self createFaceDetectorMlKit]; + self.barcodeDetector = [self createBarcodeDetectorMlKit]; + self.finishedReadingText = true; + self.finishedDetectingFace = true; + self.startText = [NSDate date]; + self.startFace = [NSDate date]; +#if !(TARGET_IPHONE_SIMULATOR) + self.previewLayer = + [AVCaptureVideoPreviewLayer layerWithSession:self.session]; + self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + self.previewLayer.needsDisplayOnBoundsChange = YES; +#endif + self.rectOfInterest = CGRectMake(0, 0, 1.0, 1.0); + + UITapGestureRecognizer * tapHandler=[self createTapGestureRecognizer]; + [self addGestureRecognizer:tapHandler]; + UITapGestureRecognizer * doubleTabHandler=[self createDoubleTapGestureRecognizer]; + [self addGestureRecognizer:doubleTabHandler]; + + self.autoFocus = -1; + self.exposure = -1; + self.presetCamera = AVCaptureDevicePositionUnspecified; + self.cameraId = nil; + self.isFocusedOnPoint = NO; + self.isExposedOnPoint = NO; + self.invertImageData = true; + _recordRequested = NO; + _sessionInterrupted = NO; + + // we will do other initialization after + // the view is loaded. + // This is to prevent code if the view is unused as react + // might create multiple instances of it. + // and we need to also add/remove event listeners. + + + } + return self; +} +-(UITapGestureRecognizer*)createDoubleTapGestureRecognizer +{ + UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; + doubleTapGestureRecognizer.numberOfTapsRequired = 2; + return doubleTapGestureRecognizer; + +} +-(UITapGestureRecognizer*)createTapGestureRecognizer +{ + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; + tapGestureRecognizer.numberOfTapsRequired = 1; + return tapGestureRecognizer; + +} +-(void)handleDoubleTap:(UITapGestureRecognizer*)doubleTapRecognizer { + [self handleTouch:doubleTapRecognizer isDoubleTap:true]; +} +-(void)handleTap:(UITapGestureRecognizer*)tapRecognizer { + [self handleTouch:tapRecognizer isDoubleTap:false]; +} +-(void)handleTouch:(UITapGestureRecognizer*)tapRecognizer isDoubleTap:(BOOL)isDoubleTap{ + if (tapRecognizer.state == UIGestureRecognizerStateRecognized) { + CGPoint location = [tapRecognizer locationInView:self]; + NSDictionary *tapEvent = [NSMutableDictionary dictionaryWithDictionary:@{ + @"isDoubleTab":@(isDoubleTap), + @"touchOrigin": @{ + @"x": @(location.x), + @"y": @(location.y) + } + }]; + [self onTouch:tapEvent]; + } +} +-(float) getMaxZoomFactor:(AVCaptureDevice*)device { + float maxZoom; + if(self.maxZoom > 1){ + maxZoom = MIN(self.maxZoom, device.activeFormat.videoMaxZoomFactor); + }else{ + maxZoom = device.activeFormat.videoMaxZoomFactor; + } + return maxZoom; +} + +-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer { + const CGFloat pinchVelocityDividerFactor = 5.0f; + + if (pinchRecognizer.state == UIGestureRecognizerStateChanged) { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if(device == nil){ + return; + } + NSError *error = nil; + float maxZoom = [self getMaxZoomFactor:device]; + if ([device lockForConfiguration:&error]) { + CGFloat desiredZoomFactor = device.videoZoomFactor + atan2f(pinchRecognizer.velocity, pinchVelocityDividerFactor); + // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor + device.videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, maxZoom)); + [device unlockForConfiguration]; + } else { + NSLog(@"error: %@", error); + } + } +} + +- (void)onReady:(NSDictionary *)event +{ + if (_onCameraReady) { + _onCameraReady(nil); + } +} + +- (void)onMountingError:(NSDictionary *)event +{ + if (_onMountError) { + _onMountError(event); + } +} + +- (void)onCodeRead:(NSDictionary *)event +{ + if (_onBarCodeRead) { + _onBarCodeRead(event); + } +} + +- (void)onPictureTaken:(NSDictionary *)event +{ + if (_onPictureTaken) { + _onPictureTaken(event); + } +} + +- (void)onPictureSaved:(NSDictionary *)event +{ + if (_onPictureSaved) { + _onPictureSaved(event); + } +} + +- (void)onRecordingStart:(NSDictionary *)event +{ + if (_onRecordingStart) { + _onRecordingStart(event); + } +} + +- (void)onRecordingEnd:(NSDictionary *)event +{ + if (_onRecordingEnd) { + _onRecordingEnd(event); + } +} +- (void)onTouch:(NSDictionary *)event +{ + if (_onTouch) { + _onTouch(event); + } +} + +- (void)onText:(NSDictionary *)event +{ + if (_onTextRecognized && _session) { + _onTextRecognized(event); + } +} + +- (void)onSubjectAreaChanged:(NSDictionary *)event +{ + if (_onSubjectAreaChanged) { + _onSubjectAreaChanged(event); + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + self.previewLayer.frame = self.bounds; + [self setBackgroundColor:[UIColor blackColor]]; + [self.layer insertSublayer:self.previewLayer atIndex:0]; +} + +- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex +{ + [self insertSubview:view atIndex:atIndex + 1]; // is this + 1 really necessary? + [super insertReactSubview:view atIndex:atIndex]; + return; +} + +- (void)removeReactSubview:(UIView *)subview +{ + [subview removeFromSuperview]; + [super removeReactSubview:subview]; + return; +} + + +- (void)willMoveToSuperview:(nullable UIView *)newSuperview; +{ + if(newSuperview != nil){ + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(orientationChanged:) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(audioDidInterrupted:) + name:AVAudioSessionInterruptionNotification + object:[AVAudioSession sharedInstance]]; + + + // this is not needed since RN will update our type value + // after mount to set the camera's default, and that will already + // this method + // [self initializeCaptureSessionInput]; + [self startSession]; + } + else{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionWasInterruptedNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + + [self stopSession]; + } + + [super willMoveToSuperview:newSuperview]; +} + + + +// Helper to get a device from the currently set properties (type and camera id) +// might return nil if device failed to be retrieved or is invalid +-(AVCaptureDevice*)getDevice +{ + AVCaptureDevice *captureDevice; + if(self.cameraId != nil){ + captureDevice = [RNCameraUtils deviceWithCameraId:self.cameraId]; + } + else{ + captureDevice = [RNCameraUtils deviceWithMediaType:AVMediaTypeVideo preferringPosition:self.presetCamera]; + } + return captureDevice; + +} + +// helper to return the camera's instance default preset +// this is for pictures only, and video should set another preset +// before recording. +// This default preset returns much smoother photos than High. +-(AVCaptureSessionPreset)getDefaultPreset +{ + AVCaptureSessionPreset preset = + ([self pictureSize] && [[self pictureSize] integerValue] >= 0) ? [self pictureSize] : AVCaptureSessionPresetPhoto; + + return preset; +} + +// helper to return the camera's default preset for videos +// considering what is currently set +-(AVCaptureSessionPreset)getDefaultPresetVideo +{ + // Default video quality AVCaptureSessionPresetHigh if non is provided + AVCaptureSessionPreset preset = + ([self defaultVideoQuality]) ? [RNCameraUtils captureSessionPresetForVideoResolution:[[self defaultVideoQuality] integerValue]] : AVCaptureSessionPresetHigh; + + return preset; +} + +- (void)lockDevice:(AVCaptureDevice *)device andApplySettings:(void (^)(void))applySettings { + NSError *error = nil; + + if(device == nil){ + return; + } + + if (![device lockForConfiguration:&error]) { + if (error) { + RCTLogError(@"%s: %@", __func__, error); + } + return; +} + + applySettings(); + + [device unlockForConfiguration]; +} + +-(void)updateType +{ + [self initializeCaptureSessionInput]; + [self startSession]; // will already check if session is running +} + + +- (void)updateFlashMode +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if(device == nil) { + return; + } + + if (self.flashMode == RNCameraFlashModeTorch) { + if (![device hasTorch] || ![device isTorchModeSupported:AVCaptureTorchModeOn]) { + RCTLogWarn(@"%s: device doesn't support torch mode", __func__); + return; + } + [self lockDevice:device andApplySettings:^{ + [device setFlashMode:AVCaptureFlashModeOff]; + [device setTorchMode:AVCaptureTorchModeOn]; + }]; + } else { + if (![device hasFlash] || ![device isFlashModeSupported:self.flashMode]) { + RCTLogWarn(@"%s: device doesn't support flash mode", __func__); + return; + } + + [self lockDevice:device andApplySettings:^{ + if ([device isTorchActive]) { + [device setTorchMode:AVCaptureTorchModeOff]; + } + [device setFlashMode:self.flashMode]; + }]; + } +} + +// Function to cleanup focus listeners and variables on device +// change. This is required since "defocusing" might not be +// possible on the new device, and our device reference will be +// different +- (void)cleanupFocus:(AVCaptureDevice*) previousDevice { + + self.isFocusedOnPoint = NO; + self.isExposedOnPoint = NO; + + // cleanup listeners if we had any + if(previousDevice != nil){ + + // remove event listener + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:previousDevice]; + + // cleanup device flags + [self lockDevice:previousDevice andApplySettings:^{ + previousDevice.subjectAreaChangeMonitoringEnabled = NO; + }]; + } +} + +- (void)defocusPointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + + + if (self.isFocusedOnPoint) { + + self.isFocusedOnPoint = NO; + + if(device == nil){ + return; + } + + device.subjectAreaChangeMonitoringEnabled = NO; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:device]; + + CGPoint prevPoint = [device focusPointOfInterest]; + + CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f); + + [device setFocusPointOfInterest: autofocusPoint]; + + [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + + [self onSubjectAreaChanged:@{ + @"prevPointOfInterest": @{ + @"x": @(prevPoint.x), + @"y": @(prevPoint.y) + } + }]; + } + + if(self.isExposedOnPoint){ + self.isExposedOnPoint = NO; + + if(device == nil){ + return; + } + + CGPoint exposurePoint = CGPointMake(0.5f, 0.5f); + + [device setExposurePointOfInterest: exposurePoint]; + + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } +} + +- (void)deexposePointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + + + if(self.isExposedOnPoint){ + self.isExposedOnPoint = NO; + + if(device == nil){ + return; + } + + CGPoint exposurePoint = CGPointMake(0.5f, 0.5f); + + [device setExposurePointOfInterest: exposurePoint]; + + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } +} + + +- (void)updateAutoFocusPointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if ([self.autoFocusPointOfInterest objectForKey:@"x"] && [self.autoFocusPointOfInterest objectForKey:@"y"]) { + + float xValue = [self.autoFocusPointOfInterest[@"x"] floatValue]; + float yValue = [self.autoFocusPointOfInterest[@"y"] floatValue]; + + CGPoint autofocusPoint = CGPointMake(xValue, yValue); + + if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + + [device setFocusPointOfInterest:autofocusPoint]; + [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + + if (!self.isFocusedOnPoint) { + self.isFocusedOnPoint = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(autofocusDelegate:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:device]; + device.subjectAreaChangeMonitoringEnabled = YES; + } + } else { + RCTLogWarn(@"AutoFocusPointOfInterest not supported"); + } + + if([self.autoFocusPointOfInterest objectForKey:@"autoExposure"]){ + BOOL autoExposure = [self.autoFocusPointOfInterest[@"autoExposure"] boolValue]; + + if(autoExposure){ + if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) + { + [device setExposurePointOfInterest:autofocusPoint]; + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + self.isExposedOnPoint = YES; + + } else { + RCTLogWarn(@"AutoExposurePointOfInterest not supported"); + } + } + else{ + [self deexposePointOfInterest]; + } + } + else{ + [self deexposePointOfInterest]; + } + + } else { + [self defocusPointOfInterest]; + [self deexposePointOfInterest]; + } + }]; +} + +- (void)autofocusDelegate:(NSNotification*) notification { + AVCaptureDevice* device = [notification object]; + + [self lockDevice:device andApplySettings:^{ + [self defocusPointOfInterest]; + [self deexposePointOfInterest]; + }]; +} + +- (void)updateFocusMode +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if ([device isFocusModeSupported:self.autoFocus]) { + [self lockDevice:device andApplySettings:^{ + [device setFocusMode:self.autoFocus]; + }]; + } +} + +- (void)updateFocusDepth +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if (device == nil || self.autoFocus < 0 || device.focusMode != RNCameraAutoFocusOff || device.position == RNCameraTypeFront) { + return; + } + + if (![device respondsToSelector:@selector(isLockingFocusWithCustomLensPositionSupported)] || ![device isLockingFocusWithCustomLensPositionSupported]) { + RCTLogWarn(@"%s: Setting focusDepth isn't supported for this camera device", __func__); + return; + } + + [self lockDevice:device andApplySettings:^{ + [device setFocusModeLockedWithLensPosition:self.focusDepth completionHandler:nil]; + }]; +} + +- (void)updateZoom { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + float maxZoom = [self getMaxZoomFactor:device]; + device.videoZoomFactor = (maxZoom - 1) * self.zoom + 1; + }]; +} + +- (void)updateWhiteBalance { + if (self.customWhiteBalanceSettings != nil) { + [self applyCustomWhiteBalance]; + } else { + [self applyDefaultWhiteBalance]; + } +} + +- (void)applyDefaultWhiteBalance { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if (self.whiteBalance == RNCameraWhiteBalanceAuto || ![device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { + if (self.whiteBalance != RNCameraWhiteBalanceAuto) { + RCTLogWarn(@"%s: locked whitebalance mode ist note supported. Fallback to continuous auto white balance mode", __func__); + } +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]; +#endif + } else { + AVCaptureWhiteBalanceTemperatureAndTintValues temperatureAndTint = { + .temperature = [RNCameraUtils temperatureForWhiteBalance:self.whiteBalance], + .tint = 0, + }; + AVCaptureWhiteBalanceGains rgbGains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:temperatureAndTint]; + + @try{ +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:rgbGains completionHandler:nil]; +#endif + } + @catch(NSException *exception){ + RCTLogError(@"Failed to set white balance: %@", exception); + } + } + }]; +} + +- (void)applyCustomWhiteBalance { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if (![device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { + RCTLogWarn(@"%s: locked whitebalance mode ist note supported. Fallback to continuous auto white balance mode", __func__); +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]; +#endif + } else { + AVCaptureWhiteBalanceTemperatureAndTintValues temperatureAndTint = { + .temperature = self.customWhiteBalanceSettings.temperature, + .tint = self.customWhiteBalanceSettings.tint, + }; + AVCaptureWhiteBalanceGains rgbGains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:temperatureAndTint]; + CGFloat redGain = rgbGains.redGain + self.customWhiteBalanceSettings.redGainOffset; + CGFloat greenGain = rgbGains.greenGain + self.customWhiteBalanceSettings.greenGainOffset; + CGFloat blueGain = rgbGains.blueGain + self.customWhiteBalanceSettings.blueGainOffset; + + rgbGains.redGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, redGain)); + rgbGains.greenGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, greenGain)); + rgbGains.blueGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, blueGain)); + + @try{ +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:rgbGains completionHandler:nil]; +#endif + } @catch(NSException *exception){ + RCTLogError(@"Failed to set custom white balance: %@", exception); + } + } + }]; +} + + +/// Set the AVCaptureDevice's ISO values based on RNCamera's 'exposure' value, +/// which is a float between 0 and 1 if defined by the user or -1 to indicate that no +/// selection is active. 'exposure' gets mapped to a valid ISO value between the +/// device's min/max-range of ISO-values. +/// +/// The exposure gets reset every time the user manually sets the autofocus-point in +/// 'updateAutoFocusPointOfInterest' automatically. Currently no explicit event is fired. +/// This leads to two 'exposure'-states: one here and one in the component, which is +/// fine. 'exposure' here gets only synced if 'exposure' on the js-side changes. You +/// can manually keep the state in sync by setting 'exposure' in your React-state +/// everytime the js-updateAutoFocusPointOfInterest-function gets called. +- (void)updateExposure +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + // Check that either no explicit exposure-val has been set yet + // or that it has been reset. Check for > 1 is only a guard. + if(self.exposure < 0 || self.exposure > 1){ + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + return; + } + + // Lazy init of range. + if(!self.exposureIsoMin){ self.exposureIsoMin = device.activeFormat.minISO; } + if(!self.exposureIsoMax){ self.exposureIsoMax = device.activeFormat.maxISO; } + + // Get a valid ISO-value in range from min to max. After we mapped the exposure + // (a val between 0 - 1), the result gets corrected by the offset from 0, which + // is the min-ISO-value. + float appliedExposure = (self.exposureIsoMax - self.exposureIsoMin) * self.exposure + self.exposureIsoMin; + + // Make sure we're in AVCaptureExposureModeCustom, else the ISO + duration time won't apply. + // Also make sure the device can set exposure + if([device isExposureModeSupported:AVCaptureExposureModeCustom]){ + if(device.exposureMode != AVCaptureExposureModeCustom){ + [device setExposureMode:AVCaptureExposureModeCustom]; + } + + // Only set the ISO for now, duration will be default as a change might affect frame rate. + @try{ + [device setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent ISO:appliedExposure completionHandler:nil]; + } + @catch(NSException *exception){ + RCTLogError(@"Failed to update exposure: %@", exception); + } + + } else { + RCTLog(@"Device does not support AVCaptureExposureModeCustom"); + } + }]; +} + +- (void)updatePictureSize +{ + // make sure to call this function so the right default is used if + // "None" is used + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + + +- (void)updateCaptureAudio +{ + dispatch_async(self.sessionQueue, ^{ + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + else{ + [self removeAudioCaptureSessionInput]; + } + }); +} + +- (void)takePictureWithOrientation:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject{ + [self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) { + NSMutableDictionary *tmpOptions = [options mutableCopy]; + if ([tmpOptions valueForKey:@"orientation"] == nil) { + tmpOptions[@"orientation"] = [NSNumber numberWithInteger:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation:orientation]]; + } + self.deviceOrientation = [NSNumber numberWithInteger:orientation]; + self.orientation = [NSNumber numberWithInteger:[tmpOptions[@"orientation"] integerValue]]; + [self takePicture:tmpOptions resolve:resolve reject:reject]; + }]; +} + +- (void)takePicture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject +{ + // if video device is not set, reject + if(self.videoCaptureDeviceInput == nil || !self.session.isRunning){ + reject(@"E_IMAGE_CAPTURE_FAILED", @"Camera is not ready.", nil); + return; + } + + if (!self.deviceOrientation) { + [self takePictureWithOrientation:options resolve:resolve reject:reject]; + return; + } + + NSInteger orientation = [options[@"orientation"] integerValue]; + + AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; + [connection setVideoOrientation:orientation]; + @try { + [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { + if (imageSampleBuffer && !error) { + + if ([options[@"pauseAfterCapture"] boolValue]) { + [[self.previewLayer connection] setEnabled:NO]; + } + + BOOL useFastMode = [options valueForKey:@"fastMode"] != nil && [options[@"fastMode"] boolValue]; + if (useFastMode) { + resolve(nil); + } + + [self onPictureTaken:@{}]; + + + // get JPEG image data + NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; + UIImage *takenImage = [UIImage imageWithData:imageData]; + + + // Adjust/crop image based on preview dimensions + // TODO: This seems needed because iOS does not allow + // for aspect ratio settings, so this is the best we can get + // to mimic android's behaviour. + CGImageRef takenCGImage = takenImage.CGImage; + CGSize previewSize; + if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) { + previewSize = CGSizeMake(self.previewLayer.frame.size.height, self.previewLayer.frame.size.width); + } else { + previewSize = CGSizeMake(self.previewLayer.frame.size.width, self.previewLayer.frame.size.height); + } + CGRect cropRect = CGRectMake(0, 0, CGImageGetWidth(takenCGImage), CGImageGetHeight(takenCGImage)); + CGRect croppedSize = AVMakeRectWithAspectRatioInsideRect(previewSize, cropRect); + takenImage = [RNImageUtils cropImage:takenImage toRect:croppedSize]; + + // apply other image settings + bool resetOrientation = NO; + if ([options[@"mirrorImage"] boolValue]) { + takenImage = [RNImageUtils mirrorImage:takenImage]; + } + if ([options[@"forceUpOrientation"] boolValue]) { + takenImage = [RNImageUtils forceUpOrientation:takenImage]; + resetOrientation = YES; + } + if ([options[@"width"] integerValue]) { + takenImage = [RNImageUtils scaleImage:takenImage toWidth:[options[@"width"] integerValue]]; + resetOrientation = YES; + } + + // get image metadata so we can re-add it later + // make it mutable since we need to adjust quality/compression + CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); + + CFMutableDictionaryRef mutableMetaDict = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); + + // release the meta dict now that we've copied it + // to Objective-C land + CFRelease(metaDict); + + // bridge the copy for auto release + NSMutableDictionary *metadata = (NSMutableDictionary *)CFBridgingRelease(mutableMetaDict); + + + // Get final JPEG image and set compression + float quality = [options[@"quality"] floatValue]; + [metadata setObject:@(quality) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality]; + + // Reset exif orientation if we need to due to image changes + // that already rotate the image. + // Other dimension attributes will be set automatically + // regardless of what we have on our metadata dict + if (resetOrientation){ + metadata[(NSString*)kCGImagePropertyOrientation] = @(1); + } + + + // get our final image data with added metadata + // idea taken from: https://stackoverflow.com/questions/9006759/how-to-write-exif-metadata-to-an-image-not-the-camera-roll-just-a-uiimage-or-j/9091472 + NSMutableData * destData = [NSMutableData data]; + + CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destData, kUTTypeJPEG, 1, NULL); + + // defaults to true, must like Android + bool writeExif = true; + + if(options[@"writeExif"]){ + + // if we received an object, merge with our meta + if ([options[@"writeExif"] isKindOfClass:[NSDictionary class]]){ + NSDictionary *newExif = options[@"writeExif"]; + + // need to update both, since apple splits data + // across exif and tiff dicts. No problems with duplicates + // they will be handled appropiately. + NSMutableDictionary *exif = metadata[(NSString*)kCGImagePropertyExifDictionary]; + + NSMutableDictionary *tiff = metadata[(NSString*)kCGImagePropertyTIFFDictionary]; + + + // initialize exif dict if not built + if(!exif){ + exif = [[NSMutableDictionary alloc] init]; + metadata[(NSString*)kCGImagePropertyExifDictionary] = exif; + } + + if(!tiff){ + tiff = [[NSMutableDictionary alloc] init]; + metadata[(NSString*)kCGImagePropertyTIFFDictionary] = exif; + } + + // merge new exif info + [exif addEntriesFromDictionary:newExif]; + [tiff addEntriesFromDictionary:newExif]; + + + // correct any GPS metadata like Android does + // need to get the right format for each value. + NSMutableDictionary *gpsDict = [[NSMutableDictionary alloc] init]; + + if(newExif[@"GPSLatitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSLatitude] = @(fabs([newExif[@"GPSLatitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSLatitudeRef] = [newExif[@"GPSLatitude"] floatValue] >= 0 ? @"N" : @"S"; + + } + if(newExif[@"GPSLongitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSLongitude] = @(fabs([newExif[@"GPSLongitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSLongitudeRef] = [newExif[@"GPSLongitude"] floatValue] >= 0 ? @"E" : @"W"; + } + if(newExif[@"GPSAltitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSAltitude] = @(fabs([newExif[@"GPSAltitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSAltitudeRef] = [newExif[@"GPSAltitude"] floatValue] >= 0 ? @(0) : @(1); + } + + // if we don't have gps info, add it + // otherwise, merge it + if(!metadata[(NSString *)kCGImagePropertyGPSDictionary]){ + metadata[(NSString *)kCGImagePropertyGPSDictionary] = gpsDict; + } + else{ + [metadata[(NSString *)kCGImagePropertyGPSDictionary] addEntriesFromDictionary:gpsDict]; + } + + } + else{ + writeExif = [options[@"writeExif"] boolValue]; + } + + } + + CGImageDestinationAddImage(destination, takenImage.CGImage, writeExif ? ((__bridge CFDictionaryRef) metadata) : nil); + + + // write final image data with metadata to our destination + if (CGImageDestinationFinalize(destination)){ + + NSMutableDictionary *response = [[NSMutableDictionary alloc] init]; + + NSString *path = nil; + if (options[@"path"]) { + path = options[@"path"]; + } + else{ + path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".jpg"]; + } + + if (![options[@"doNotSave"] boolValue]) { + response[@"uri"] = [RNImageUtils writeImage:destData toPath:path]; + } + response[@"width"] = @(takenImage.size.width); + response[@"height"] = @(takenImage.size.height); + + if ([options[@"base64"] boolValue]) { + response[@"base64"] = [destData base64EncodedStringWithOptions:0]; + } + + if ([options[@"exif"] boolValue]) { + response[@"exif"] = metadata; + + // No longer needed since we always get the photo metadata now + //[RNImageUtils updatePhotoMetadata:imageSampleBuffer withAdditionalData:@{ @"Orientation": @(imageRotation) } inResponse:response]; // TODO + } + + response[@"pictureOrientation"] = @([self.orientation integerValue]); + response[@"deviceOrientation"] = @([self.deviceOrientation integerValue]); + self.orientation = nil; + self.deviceOrientation = nil; + + if (useFastMode) { + [self onPictureSaved:@{@"data": response, @"id": options[@"id"]}]; + } else { + resolve(response); + } + } + else{ + reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be saved", error); + } + + // release image resource + @try{ + CFRelease(destination); + } + @catch(NSException *exception){ + RCTLogError(@"Failed to release CGImageDestinationRef: %@", exception); + } + + } else { + reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be captured", error); + } + }]; + } @catch (NSException *exception) { + reject( + @"E_IMAGE_CAPTURE_FAILED", + @"Got exception while taking picture", + [NSError errorWithDomain:@"E_IMAGE_CAPTURE_FAILED" code: 500 userInfo:@{NSLocalizedDescriptionKey:exception.reason}] + ); + } +} + +- (void)recordWithOrientation:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject{ + [self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) { + NSMutableDictionary *tmpOptions = [options mutableCopy]; + if ([tmpOptions valueForKey:@"orientation"] == nil) { + tmpOptions[@"orientation"] = [NSNumber numberWithInteger:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation: orientation]]; + } + self.deviceOrientation = [NSNumber numberWithInteger:orientation]; + self.orientation = [NSNumber numberWithInteger:[tmpOptions[@"orientation"] integerValue]]; + [self record:tmpOptions resolve:resolve reject:reject]; + }]; +} +- (void)record:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject +{ + if(self.videoCaptureDeviceInput == nil || !self.session.isRunning){ + reject(@"E_VIDEO_CAPTURE_FAILED", @"Camera is not ready.", nil); + return; + } + + if (!self.deviceOrientation) { + [self recordWithOrientation:options resolve:resolve reject:reject]; + return; + } + + NSInteger orientation = [options[@"orientation"] integerValue]; + + // some operations will change our config + // so we batch config updates, even if inner calls + // might also call this, only the outermost commit will take effect + // making the camera changes much faster. + [self.session beginConfiguration]; + + + if (_movieFileOutput == nil) { + // At the time of writing AVCaptureMovieFileOutput and AVCaptureVideoDataOutput (> GMVDataOutput) + // cannot coexist on the same AVSession (see: https://stackoverflow.com/a/4986032/1123156). + // We stop face detection here and restart it in when AVCaptureMovieFileOutput finishes recording. + if ([self.textDetector isRealDetector]) { + [self stopTextRecognition]; + } + if ([self.faceDetector isRealDetector]) { + [self stopFaceDetection]; + } + if ([self.barcodeDetector isRealDetector]) { + [self stopBarcodeDetection]; + } + [self setupMovieFileCapture]; + } + + if (self.movieFileOutput == nil || self.movieFileOutput.isRecording || _videoRecordedResolve != nil || _videoRecordedReject != nil) { + [self.session commitConfiguration]; + return; + } + + + // video preset will be cleanedup/restarted once capture is done + // with a camera cleanup call + if (options[@"quality"]) { + AVCaptureSessionPreset newQuality = [RNCameraUtils captureSessionPresetForVideoResolution:(RNCameraVideoResolution)[options[@"quality"] integerValue]]; + if (self.session.sessionPreset != newQuality) { + [self updateSessionPreset:newQuality]; + } + } + else{ + AVCaptureSessionPreset newQuality = [self getDefaultPresetVideo]; + if (self.session.sessionPreset != newQuality) { + [self updateSessionPreset:newQuality]; + } + } + + AVCaptureConnection *connection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; + + if (self.videoStabilizationMode != 0) { + if (connection.isVideoStabilizationSupported == NO) { + RCTLogWarn(@"%s: Video Stabilization is not supported on this device.", __func__); + } else { + [connection setPreferredVideoStabilizationMode:self.videoStabilizationMode]; + } + } + [connection setVideoOrientation:orientation]; + + BOOL recordAudio = [options valueForKey:@"mute"] == nil || ([options valueForKey:@"mute"] != nil && ![options[@"mute"] boolValue]); + + // sound recording connection, we can easily turn it on/off without manipulating inputs, this prevents flickering. + // note that mute will also be set to true + // if captureAudio is set to false on the JS side. + // Check the property anyways just in case it is manipulated + // with setNativeProps + if(recordAudio && self.captureAudio){ + + // if we haven't initialized our capture session yet + // initialize it. This will cause video to flicker. + [self initializeAudioCaptureSessionInput]; + + + // finally, make sure we got access to the capture device + // and turn the connection on. + if(self.audioCaptureDeviceInput != nil){ + AVCaptureConnection *audioConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeAudio]; + audioConnection.enabled = YES; + } + + } + + // if we have a capture input but are muted + // disable connection. No flickering here. + else if(self.audioCaptureDeviceInput != nil){ + AVCaptureConnection *audioConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeAudio]; + audioConnection.enabled = NO; + } + + dispatch_async(self.sessionQueue, ^{ + + // session preset might affect this, so we run this code + // also in the session queue + + if (options[@"maxDuration"]) { + Float64 maxDuration = [options[@"maxDuration"] floatValue]; + self.movieFileOutput.maxRecordedDuration = CMTimeMakeWithSeconds(maxDuration, 30); + } + + if (options[@"maxFileSize"]) { + self.movieFileOutput.maxRecordedFileSize = [options[@"maxFileSize"] integerValue]; + } + + if (options[@"fps"]) { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + AVCaptureDeviceFormat *activeFormat = device.activeFormat; + CMFormatDescriptionRef activeDescription = activeFormat.formatDescription; + CMVideoDimensions activeDimensions = CMVideoFormatDescriptionGetDimensions(activeDescription); + + NSInteger fps = [options[@"fps"] integerValue]; + CGFloat desiredFPS = (CGFloat)fps; + + AVCaptureDeviceFormat *selectedFormat = nil; + int32_t activeWidth = activeDimensions.width; + int32_t maxWidth = 0; + + for (AVCaptureDeviceFormat *format in [device formats]) { + CMFormatDescriptionRef formatDescription = format.formatDescription; + CMVideoDimensions formatDimensions = CMVideoFormatDescriptionGetDimensions(formatDescription); + int32_t formatWidth = formatDimensions.width; + if (formatWidth != activeWidth || formatWidth < maxWidth) { + continue; + } + + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { + if (range.minFrameRate <= desiredFPS && desiredFPS <= range.maxFrameRate) { + selectedFormat = format; + maxWidth = formatWidth; + } + } + } + + if (selectedFormat) { + if ([device lockForConfiguration:nil]) { + device.activeFormat = selectedFormat; + device.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)desiredFPS); + device.activeVideoMaxFrameDuration = CMTimeMake(1, (int32_t)desiredFPS); + [device unlockForConfiguration]; + } + } else { + RCTLog(@"We could not find a suitable format for this device."); + } + } + + if (options[@"codec"]) { + if (@available(iOS 10, *)) { + AVVideoCodecType videoCodecType = options[@"codec"]; + if ([self.movieFileOutput.availableVideoCodecTypes containsObject:videoCodecType]) { + self.videoCodecType = videoCodecType; + if(options[@"videoBitrate"]) { + NSString *videoBitrate = options[@"videoBitrate"]; + [self.movieFileOutput setOutputSettings:@{ + AVVideoCodecKey:videoCodecType, + AVVideoCompressionPropertiesKey: + @{ + AVVideoAverageBitRateKey:videoBitrate + } + } forConnection:connection]; + } else { + [self.movieFileOutput setOutputSettings:@{AVVideoCodecKey:videoCodecType} forConnection:connection]; + } + } else { + RCTLogWarn(@"Video Codec %@ is not available.", videoCodecType); + } + } + else { + RCTLogWarn(@"%s: Setting videoCodec is only supported above iOS version 10.", __func__); + } + } + + NSString *path = nil; + if (options[@"path"]) { + path = options[@"path"]; + } + else { + path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".mov"]; + } + + if ([options[@"mirrorVideo"] boolValue]) { + if ([connection isVideoMirroringSupported]) { + [connection setAutomaticallyAdjustsVideoMirroring:NO]; + [connection setVideoMirrored:YES]; + } + } + + // finally, commit our config changes before starting to record + [self.session commitConfiguration]; + + // and update flash in case it was turned off automatically + // due to session/preset changes + [self updateFlashMode]; + + // after everything is set, start recording with a tiny delay + // to ensure the camera already has focus and exposure set. + double delayInSeconds = 0.5; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); + + // we will use this flag to stop recording + // if it was requested to stop before it could even start + _recordRequested = YES; + + dispatch_after(popTime, self.sessionQueue, ^(void){ + + // our session might have stopped in between the timeout + // so make sure it is still valid, otherwise, error and cleanup + if(self.movieFileOutput != nil && self.videoCaptureDeviceInput != nil && _recordRequested){ + NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:path]; + [self.movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self]; + self.videoRecordedResolve = resolve; + self.videoRecordedReject = reject; + + [self onRecordingStart:@{ + @"uri": outputURL.absoluteString, + @"videoOrientation": @([self.orientation integerValue]), + @"deviceOrientation": @([self.deviceOrientation integerValue]) + }]; + + } + else{ + reject(@"E_VIDEO_CAPTURE_FAILED", !_recordRequested ? @"Recording request cancelled." : @"Camera is not ready.", nil); + [self cleanupCamera]; + } + + // reset our flag + _recordRequested = NO; + }); + + + }); +} + +- (void)stopRecording +{ + dispatch_async(self.sessionQueue, ^{ + if ([self.movieFileOutput isRecording]) { + [self.movieFileOutput stopRecording]; + [self onRecordingEnd:@{}]; + } else { + if(_recordRequested){ + _recordRequested = NO; + } + else{ + RCTLogWarn(@"Video is not recording."); + } + } + }); +} + +- (void)resumePreview +{ + [[self.previewLayer connection] setEnabled:YES]; +} + +- (void)pausePreview +{ + [[self.previewLayer connection] setEnabled:NO]; +} + +- (void)startSession +{ +#if TARGET_IPHONE_SIMULATOR + [self onReady:nil]; + return; +#endif + dispatch_async(self.sessionQueue, ^{ + + // if session already running, also return and fire ready event + // this is helpfu when the device type or ID is changed and we must + // receive another ready event (like Android does) + if(self.session.isRunning){ + [self onReady:nil]; + return; + } + + // if camera not set (invalid type and no ID) return. + if (self.presetCamera == AVCaptureDevicePositionUnspecified && self.cameraId == nil) { + return; + } + + // video device was not initialized, also return + if(self.videoCaptureDeviceInput == nil){ + return; + } + + + AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; + if ([self.session canAddOutput:stillImageOutput]) { + stillImageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG}; + [self.session addOutput:stillImageOutput]; + [stillImageOutput setHighResolutionStillImageOutputEnabled:YES]; + self.stillImageOutput = stillImageOutput; + } + + // If AVCaptureVideoDataOutput is not required because of Google Vision + // (see comment in -record), we go ahead and add the AVCaptureMovieFileOutput + // to avoid an exposure rack on some devices that can cause the first few + // frames of the recorded output to be underexposed. + if (![self.faceDetector isRealDetector] && ![self.textDetector isRealDetector] && ![self.barcodeDetector isRealDetector]) { + [self setupMovieFileCapture]; + } + [self setupOrDisableBarcodeScanner]; + + _sessionInterrupted = NO; + [self.session startRunning]; + [self onReady:nil]; + }); +} + +- (void)stopSession +{ +#if TARGET_IPHONE_SIMULATOR + return; +#endif + dispatch_async(self.sessionQueue, ^{ + if ([self.textDetector isRealDetector]) { + [self stopTextRecognition]; + } + if ([self.faceDetector isRealDetector]) { + [self stopFaceDetection]; + } + if ([self.barcodeDetector isRealDetector]) { + [self stopBarcodeDetection]; + } + [self.previewLayer removeFromSuperlayer]; + [self.session commitConfiguration]; + [self.session stopRunning]; + + for (AVCaptureInput *input in self.session.inputs) { + [self.session removeInput:input]; + } + + for (AVCaptureOutput *output in self.session.outputs) { + [self.session removeOutput:output]; + } + + // cleanup audio input if any, and release + // audio session so other apps can continue playback. + [self removeAudioCaptureSessionInput]; + + // clean these up as well since we've removed + // all inputs and outputs from session + self.videoCaptureDeviceInput = nil; + self.audioCaptureDeviceInput = nil; + self.movieFileOutput = nil; + }); +} + +// Initializes audio capture device +// Note: Ensure this is called within a a session configuration block +- (void)initializeAudioCaptureSessionInput +{ + // only initialize if not initialized already + if(self.audioCaptureDeviceInput == nil){ + NSError *error = nil; + + AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; + AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error]; + + if (error || audioDeviceInput == nil) { + RCTLogWarn(@"%s: %@", __func__, error); + } + + else{ + + // test if we can activate the device input. + // If we fail, means it is already being used + BOOL setActive = [[AVAudioSession sharedInstance] setActive:YES error:&error]; + + if (!setActive) { + RCTLogWarn(@"Audio device could not set active: %s: %@", __func__, error); + } + + else if ([self.session canAddInput:audioDeviceInput]) { + [self.session addInput:audioDeviceInput]; + self.audioCaptureDeviceInput = audioDeviceInput; + + // inform that audio has been resumed + if(self.onAudioConnected){ + self.onAudioConnected(nil); + } + } + else{ + RCTLog(@"Cannot add audio input"); + } + } + + // if we failed to get the audio device, fire our interrupted event + if(self.audioCaptureDeviceInput == nil && self.onAudioInterrupted){ + self.onAudioInterrupted(nil); + } + } +} + + +// Removes audio capture from the session, allowing the session +// to resume if it was interrupted, and stopping any +// recording in progress with the appropriate flags. +- (void)removeAudioCaptureSessionInput +{ + if(self.audioCaptureDeviceInput != nil){ + + BOOL audioRemoved = NO; + + if ([self.session.inputs containsObject:self.audioCaptureDeviceInput]) { + + if ([self isRecording]) { + self.isRecordingInterrupted = YES; + } + + [self.session removeInput:self.audioCaptureDeviceInput]; + + self.audioCaptureDeviceInput = nil; + + // update flash since it gets reset when + // we change the session inputs + dispatch_async(self.sessionQueue, ^{ + [self updateFlashMode]; + }); + + audioRemoved = YES; + } + + // Deactivate our audio session so other audio can resume + // playing, if any. E.g., background music. + // unless told not to + if(!self.keepAudioSession){ + NSError *error = nil; + + BOOL setInactive = [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]; + + if (!setInactive) { + RCTLogWarn(@"Audio device could not set inactive: %s: %@", __func__, error); + } + } + + self.audioCaptureDeviceInput = nil; + + // inform that audio was interrupted + if(audioRemoved && self.onAudioInterrupted){ + self.onAudioInterrupted(nil); + } + } +} + + +- (void)initializeCaptureSessionInput +{ + + dispatch_async(self.sessionQueue, ^{ + + // Do all camera initialization in the session queue + // to prevent it from + AVCaptureDevice *captureDevice = [self getDevice]; + + // if setting a new device is the same we currently have, nothing to do + // return. + if(self.videoCaptureDeviceInput != nil && captureDevice != nil && [self.videoCaptureDeviceInput.device.uniqueID isEqualToString:captureDevice.uniqueID]){ + return; + } + + // if the device we are setting is also invalid/nil, return + if(captureDevice == nil){ + [self onMountingError:@{@"message": @"Invalid camera device."}]; + return; + } + + // get orientation also in our session queue to prevent + // race conditions and also blocking the main thread + __block UIInterfaceOrientation interfaceOrientation; + + dispatch_sync(dispatch_get_main_queue(), ^{ + interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; + }); + + AVCaptureVideoOrientation orientation = [RNCameraUtils videoOrientationForInterfaceOrientation:interfaceOrientation]; + + + [self.session beginConfiguration]; + + NSError *error = nil; + AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; + + if(error != nil){ + NSLog(@"Capture device error %@", error); + } + + if (error || captureDeviceInput == nil) { + RCTLog(@"%s: %@", __func__, error); + [self.session commitConfiguration]; + [self onMountingError:@{@"message": @"Failed to setup capture device."}]; + return; + } + + + // Do additional cleanup that might be needed on the + // previous device, if any. + AVCaptureDevice *previousDevice = self.videoCaptureDeviceInput != nil ? self.videoCaptureDeviceInput.device : nil; + + [self cleanupFocus:previousDevice]; + + + // Remove inputs + [self.session removeInput:self.videoCaptureDeviceInput]; + + // clear this variable before setting it again. + // Otherwise, if setting fails, we end up with a stale value. + // and we are no longer able to detect if it changed or not + self.videoCaptureDeviceInput = nil; + + // setup our capture preset based on what was set from RN + // and our defaults + // if the preset is not supported (e.g., when switching cameras) + // canAddInput below will fail + self.session.sessionPreset = [self getDefaultPreset]; + + + // reset iso cached values, these might be different + // from camera to camera. Otherwise, the camera may crash + // when changing cameras and exposure. + self.exposureIsoMin = 0; + self.exposureIsoMax = 0; + + + if ([self.session canAddInput:captureDeviceInput]) { + [self.session addInput:captureDeviceInput]; + + self.videoCaptureDeviceInput = captureDeviceInput; + + // Update all these async after our session has commited + // since some values might be changed on session commit. + dispatch_async(self.sessionQueue, ^{ + [self updateZoom]; + [self updateFocusMode]; + [self updateFocusDepth]; + [self updateExposure]; + [self updateAutoFocusPointOfInterest]; + [self updateWhiteBalance]; + [self updateFlashMode]; + }); + + [self.previewLayer.connection setVideoOrientation:orientation]; + [self _updateMetadataObjectsToRecognize]; + } + else{ + RCTLog(@"The selected device does not work with the Preset [%@] or configuration provided", self.session.sessionPreset); + + [self onMountingError:@{@"message": @"Camera device does not support selected settings."}]; + } + + + // if we have not yet set our audio capture device, + // set it. Setting it early will prevent flickering when + // recording a video + // Only set it if captureAudio is true so we don't prompt + // for permission if audio is not needed. + // TODO: If we can update checkRecordAudioAuthorizationStatus + // to actually do something in production, we can replace + // the captureAudio prop by a simple permission check; + // for example, checking + // [[AVAudioSession sharedInstance] recordPermission] == AVAudioSessionRecordPermissionGranted + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + + [self.session commitConfiguration]; + }); +} + +#pragma mark - internal + +- (void)updateSessionPreset:(AVCaptureSessionPreset)preset +{ +#if !(TARGET_IPHONE_SIMULATOR) + if ([preset integerValue] < 0) { + return; + } + if (preset) { + if (self.canDetectFaces && [preset isEqual:AVCaptureSessionPresetPhoto]) { + RCTLog(@"AVCaptureSessionPresetPhoto not supported during face detection. Falling back to AVCaptureSessionPresetHigh"); + preset = AVCaptureSessionPresetHigh; + } + dispatch_async(self.sessionQueue, ^{ + if ([self.session canSetSessionPreset:preset]) { + [self.session beginConfiguration]; + self.session.sessionPreset = preset; + [self.session commitConfiguration]; + + // Need to update these since it gets reset on preset change + [self updateFlashMode]; + [self updateZoom]; + } + else{ + RCTLog(@"The selected preset [%@] does not work with the current session.", preset); + } + }); + } +#endif +} + + +// We are using this event to detect audio interruption ended +// events since we won't receive it on our session +// after disabling audio. +- (void)audioDidInterrupted:(NSNotification *)notification +{ + NSDictionary *userInfo = notification.userInfo; + NSInteger type = [[userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + + + // if our audio interruption ended + if(type == AVAudioSessionInterruptionTypeEnded){ + + // and the end event contains a hint that we should resume + // audio. Then re-connect our audio session if we are + // capturing audio. + // Sometimes we are hinted to not resume audio; e.g., + // when playing music in background. + + NSInteger option = [[userInfo valueForKey:AVAudioSessionInterruptionOptionKey] integerValue]; + + if(self.captureAudio && option == AVAudioSessionInterruptionOptionShouldResume){ + + dispatch_async(self.sessionQueue, ^{ + + // initialize audio if we need it + // check again captureAudio in case it was changed + // in between + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + }); + } + + } +} + + +// session interrupted events +- (void)sessionWasInterrupted:(NSNotification *)notification +{ + // Mark session interruption + _sessionInterrupted = YES; + + // Turn on video interrupted if our session is interrupted + // for any reason + if ([self isRecording]) { + self.isRecordingInterrupted = YES; + } + + // prevent any video recording start that we might have on the way + _recordRequested = NO; + + // get event info and fire RN event if our session was interrupted + // due to audio being taken away. + NSDictionary *userInfo = notification.userInfo; + NSInteger type = [[userInfo valueForKey:AVCaptureSessionInterruptionReasonKey] integerValue]; + + if(type == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient){ + // if we have audio, stop it so preview resumes + // it will eventually be re-loaded the next time recording + // is requested, although it will flicker. + dispatch_async(self.sessionQueue, ^{ + [self removeAudioCaptureSessionInput]; + }); + + } + +} + + +// update flash and our interrupted flag on session resume +- (void)sessionDidStartRunning:(NSNotification *)notification +{ + //NSLog(@"sessionDidStartRunning Was interrupted? %d", _sessionInterrupted); + + if(_sessionInterrupted){ + // resume flash value since it will be resetted / turned off + dispatch_async(self.sessionQueue, ^{ + [self updateFlashMode]; + }); + } + + _sessionInterrupted = NO; +} + +- (void)sessionRuntimeError:(NSNotification *)notification +{ + // Manually restarting the session since it must + // have been stopped due to an error. + dispatch_async(self.sessionQueue, ^{ + _sessionInterrupted = NO; + [self.session startRunning]; + [self onReady:nil]; + }); +} + +- (void)orientationChanged:(NSNotification *)notification +{ + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + [self changePreviewOrientation:orientation]; +} + +- (void)changePreviewOrientation:(UIInterfaceOrientation)orientation +{ + __weak typeof(self) weakSelf = self; + AVCaptureVideoOrientation videoOrientation = [RNCameraUtils videoOrientationForInterfaceOrientation:orientation]; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(self) strongSelf = weakSelf; + if (strongSelf && strongSelf.previewLayer.connection.isVideoOrientationSupported) { + [strongSelf.previewLayer.connection setVideoOrientation:videoOrientation]; + } + }); +} +-(UIPinchGestureRecognizer*)createUIPinchGestureRecognizer +{ + return [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchToZoomRecognizer:)]; +} +- (void)setupOrDisablePinchZoom +{ + if([self useNativeZoom]){ + self.pinchGestureRecognizer=[self createUIPinchGestureRecognizer]; + [self addGestureRecognizer:self.pinchGestureRecognizer]; + }else{ + [self removeGestureRecognizer:self.pinchGestureRecognizer]; + self.pinchGestureRecognizer=nil; + } +} + +# pragma mark - AVCaptureMetadataOutput + +- (void)setupOrDisableBarcodeScanner +{ + [self _setupOrDisableMetadataOutput]; + [self _updateMetadataObjectsToRecognize]; +} + +- (void)updateRectOfInterest +{ + if (_metadataOutput == nil) { + return; + } + [_metadataOutput setRectOfInterest: _rectOfInterest]; +} + +- (void)_setupOrDisableMetadataOutput +{ + if ([self isReadingBarCodes] && (_metadataOutput == nil || ![self.session.outputs containsObject:_metadataOutput])) { + AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init]; + if ([self.session canAddOutput:metadataOutput]) { + [metadataOutput setMetadataObjectsDelegate:self queue:self.sessionQueue]; + [self.session addOutput:metadataOutput]; + self.metadataOutput = metadataOutput; + } + } else if (_metadataOutput != nil && ![self isReadingBarCodes]) { + [self.session removeOutput:_metadataOutput]; + _metadataOutput = nil; + } +} + +- (void)_updateMetadataObjectsToRecognize +{ + if (_metadataOutput == nil) { + return; + } + + NSArray *availableRequestedObjectTypes = [[NSArray alloc] init]; + NSArray *requestedObjectTypes = [NSArray arrayWithArray:self.barCodeTypes]; + NSArray *availableObjectTypes = _metadataOutput.availableMetadataObjectTypes; + + for(AVMetadataObjectType objectType in requestedObjectTypes) { + if ([availableObjectTypes containsObject:objectType]) { + availableRequestedObjectTypes = [availableRequestedObjectTypes arrayByAddingObject:objectType]; + } + } + + [_metadataOutput setMetadataObjectTypes:availableRequestedObjectTypes]; + [self updateRectOfInterest]; +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects + fromConnection:(AVCaptureConnection *)connection +{ + for(AVMetadataObject *metadata in metadataObjects) { + if([metadata isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) { + AVMetadataMachineReadableCodeObject *codeMetadata = (AVMetadataMachineReadableCodeObject *) metadata; + for (id barcodeType in self.barCodeTypes) { + if ([metadata.type isEqualToString:barcodeType]) { + AVMetadataMachineReadableCodeObject *transformed = (AVMetadataMachineReadableCodeObject *)[_previewLayer transformedMetadataObjectForMetadataObject:metadata]; + NSMutableDictionary *event = [NSMutableDictionary dictionaryWithDictionary:@{ + @"type" : codeMetadata.type, + @"data" : [NSNull null], + @"rawData" : [NSNull null], + @"bounds": @{ + @"origin": @{ + @"x": [NSString stringWithFormat:@"%f", transformed.bounds.origin.x], + @"y": [NSString stringWithFormat:@"%f", transformed.bounds.origin.y] + }, + @"size": @{ + @"height": [NSString stringWithFormat:@"%f", transformed.bounds.size.height], + @"width": [NSString stringWithFormat:@"%f", transformed.bounds.size.width] + } + } + } + ]; + + NSData *rawData; + // If we're on ios11 then we can use `descriptor` to access the raw data of the barcode. + // If we're on an older version of iOS we're stuck using valueForKeyPath to peak at the + // data. + if (@available(iOS 11, *)) { + // descriptor is a CIBarcodeDescriptor which is an abstract base class with no useful fields. + // in practice it's a subclass, many of which contain errorCorrectedPayload which is the data we + // want. Instead of individually checking the class types, just duck type errorCorrectedPayload + if ([codeMetadata.descriptor respondsToSelector:@selector(errorCorrectedPayload)]) { + rawData = [codeMetadata.descriptor performSelector:@selector(errorCorrectedPayload)]; + } + } else { + rawData = [codeMetadata valueForKeyPath:@"_internal.basicDescriptor.BarcodeRawData"]; + } + + // Now that we have the raw data of the barcode translate it into a hex string to pass to the JS + const unsigned char *dataBuffer = (const unsigned char *)[rawData bytes]; + if (dataBuffer) { + NSMutableString *rawDataHexString = [NSMutableString stringWithCapacity:([rawData length] * 2)]; + for (int i = 0; i < [rawData length]; ++i) { + [rawDataHexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; + } + [event setObject:[NSString stringWithString:rawDataHexString] forKey:@"rawData"]; + } + + // If we were able to extract a string representation of the barcode, attach it to the event as well + // else just send null along. + if (codeMetadata.stringValue) { + [event setObject:codeMetadata.stringValue forKey:@"data"]; + } + + // Only send the event if we were able to pull out a binary or string representation + if ([event objectForKey:@"data"] != [NSNull null] || [event objectForKey:@"rawData"] != [NSNull null]) { + [self onCodeRead:event]; + } + } + } + } + } +} + +# pragma mark - AVCaptureMovieFileOutput + +- (void)setupMovieFileCapture +{ + AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; + + if ([self.session canAddOutput:movieFileOutput]) { + [self.session addOutput:movieFileOutput]; + self.movieFileOutput = movieFileOutput; + } +} + +- (void)cleanupMovieFileCapture +{ + if ([_session.outputs containsObject:_movieFileOutput]) { + [_session removeOutput:_movieFileOutput]; + _movieFileOutput = nil; + } +} + +- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error +{ + BOOL success = YES; + if ([error code] != noErr) { + NSNumber *value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey]; + if (value) { + success = [value boolValue]; + } + } + if (success && self.videoRecordedResolve != nil) { + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + + void (^resolveBlock)(void) = ^() { + self.videoRecordedResolve(result); + }; + + result[@"uri"] = outputFileURL.absoluteString; + result[@"videoOrientation"] = @([self.orientation integerValue]); + result[@"deviceOrientation"] = @([self.deviceOrientation integerValue]); + result[@"isRecordingInterrupted"] = @(self.isRecordingInterrupted); + + + if (@available(iOS 10, *)) { + AVVideoCodecType videoCodec = self.videoCodecType; + if (videoCodec == nil) { + videoCodec = [self.movieFileOutput.availableVideoCodecTypes firstObject]; + } + result[@"codec"] = videoCodec; + + if ([connections[0] isVideoMirrored]) { + [self mirrorVideo:outputFileURL completion:^(NSURL *mirroredURL) { + result[@"uri"] = mirroredURL.absoluteString; + resolveBlock(); + }]; + return; + } + } + + resolveBlock(); + } else if (self.videoRecordedReject != nil) { + self.videoRecordedReject(@"E_RECORDING_FAILED", @"An error occurred while recording a video.", error); + } + + [self cleanupCamera]; + +} + +- (void)cleanupCamera { + self.videoRecordedResolve = nil; + self.videoRecordedReject = nil; + self.videoCodecType = nil; + self.deviceOrientation = nil; + self.orientation = nil; + self.isRecordingInterrupted = NO; + + if ([self.textDetector isRealDetector] || [self.faceDetector isRealDetector]) { + [self cleanupMovieFileCapture]; + } + + if ([self.textDetector isRealDetector]) { + [self setupOrDisableTextDetector]; + } + + if ([self.faceDetector isRealDetector]) { + [self setupOrDisableFaceDetector]; + } + + if ([self.barcodeDetector isRealDetector]) { + [self setupOrDisableBarcodeDetector]; + } + + // reset preset to current default + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)mirrorVideo:(NSURL *)inputURL completion:(void (^)(NSURL* outputUR))completion { + AVAsset* videoAsset = [AVAsset assetWithURL:inputURL]; + AVAssetTrack* clipVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject]; + + AVMutableComposition* composition = [[AVMutableComposition alloc] init]; + [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; + + AVMutableVideoComposition* videoComposition = [[AVMutableVideoComposition alloc] init]; + videoComposition.renderSize = CGSizeMake(clipVideoTrack.naturalSize.height, clipVideoTrack.naturalSize.width); + videoComposition.frameDuration = CMTimeMake(1, 30); + + AVMutableVideoCompositionLayerInstruction* transformer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:clipVideoTrack]; + + AVMutableVideoCompositionInstruction* instruction = [[AVMutableVideoCompositionInstruction alloc] init]; + instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30)); + + CGAffineTransform transform = CGAffineTransformMakeScale(-1.0, 1.0); + transform = CGAffineTransformTranslate(transform, -clipVideoTrack.naturalSize.width, 0); + transform = CGAffineTransformRotate(transform, M_PI/2.0); + transform = CGAffineTransformTranslate(transform, 0.0, -clipVideoTrack.naturalSize.width); + + [transformer setTransform:transform atTime:kCMTimeZero]; + + [instruction setLayerInstructions:@[transformer]]; + [videoComposition setInstructions:@[instruction]]; + + // Export + AVAssetExportSession* exportSession = [AVAssetExportSession exportSessionWithAsset:videoAsset presetName:AVAssetExportPreset640x480]; + NSString* filePath = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingString:@"CameraFlip"] withExtension:@".mp4"]; + NSURL* outputURL = [NSURL fileURLWithPath:filePath]; + [exportSession setOutputURL:outputURL]; + [exportSession setOutputFileType:AVFileTypeMPEG4]; + [exportSession setVideoComposition:videoComposition]; + [exportSession exportAsynchronouslyWithCompletionHandler:^{ + if (exportSession.status == AVAssetExportSessionStatusCompleted) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(outputURL); + }); + } else { + NSLog(@"Export failed %@", exportSession.error); + } + }]; +} + +# pragma mark - FaceDetectorMlkit + +-(id)createFaceDetectorMlKit +{ + Class faceDetectorManagerClassMlkit = NSClassFromString(@"FaceDetectorManagerMlkit"); + return [[faceDetectorManagerClassMlkit alloc] init]; +} + +- (void)setupOrDisableFaceDetector +{ + if (self.canDetectFaces && [self.faceDetector isRealDetector]){ + AVCaptureSessionPreset preset = [self getDefaultPresetVideo]; + + self.session.sessionPreset = preset; + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopFaceDetection]; + return; + } + + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopFaceDetection]; + } +} + +- (void)stopFaceDetection +{ + if (self.videoDataOutput && !self.canReadText) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)updateTrackingEnabled:(id)requestedTracking +{ + [self.faceDetector setTracking:requestedTracking queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionMode:(id)requestedMode +{ + [self.faceDetector setPerformanceMode:requestedMode queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionLandmarks:(id)requestedLandmarks +{ + [self.faceDetector setLandmarksMode:requestedLandmarks queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionClassifications:(id)requestedClassifications +{ + [self.faceDetector setClassificationMode:requestedClassifications queue:self.sessionQueue]; +} + +- (void)onFacesDetected:(NSDictionary *)event +{ + if (_onFacesDetected && _session) { + _onFacesDetected(event); + } +} + +# pragma mark - BarcodeDetectorMlkit + +-(id)createBarcodeDetectorMlKit +{ + Class barcodeDetectorManagerClassMlkit = NSClassFromString(@"BarcodeDetectorManagerMlkit"); + return [[barcodeDetectorManagerClassMlkit alloc] init]; +} + +- (void)setupOrDisableBarcodeDetector +{ + if (self.canDetectBarcodes && [self.barcodeDetector isRealDetector]){ + AVCaptureSessionPreset preset = [self getDefaultPresetVideo]; + + self.session.sessionPreset = preset; + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopBarcodeDetection]; + return; + } + + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopBarcodeDetection]; + } +} + +- (void)stopBarcodeDetection +{ + if (self.videoDataOutput && !self.canReadText) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)updateGoogleVisionBarcodeType:(id)requestedTypes +{ + [self.barcodeDetector setType:requestedTypes queue:self.sessionQueue]; +} + +- (void)updateGoogleVisionBarcodeMode:(id)requestedMode +{ + if ([self.barcodeDetector isRealDetector]) { + [self.barcodeDetector setMode:requestedMode queue:self.sessionQueue]; + } +} + +- (void)onBarcodesDetected:(NSDictionary *)event +{ + if (_onGoogleVisionBarcodesDetected && _session) { + _onGoogleVisionBarcodesDetected(event); + } +} + +# pragma mark - TextDetector + +-(id)createTextDetector +{ + Class textDetectorManagerClass = NSClassFromString(@"TextDetectorManager"); + return [[textDetectorManagerClass alloc] init]; +} + +- (void)setupOrDisableTextDetector +{ + if ([self canReadText] && [self.textDetector isRealDetector]){ + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopTextRecognition]; + return; + } + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopTextRecognition]; + } +} + +- (void)stopTextRecognition +{ + if (self.videoDataOutput && !self.canDetectFaces) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; +} + +# pragma mark - mlkit + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection +{ + if (![self.textDetector isRealDetector] && ![self.faceDetector isRealDetector] && ![self.barcodeDetector isRealDetector]) { + NSLog(@"failing real check"); + return; + } + + // Do not submit image for text/face recognition too often: + // 1. we only dispatch events every 500ms anyway + // 2. wait until previous recognition is finished + // 3. let user disable text recognition, e.g. onTextRecognized={someCondition ? null : this.textRecognized} + NSDate *methodFinish = [NSDate date]; + NSTimeInterval timePassedSinceSubmittingForText = [methodFinish timeIntervalSinceDate:self.startText]; + NSTimeInterval timePassedSinceSubmittingForFace = [methodFinish timeIntervalSinceDate:self.startFace]; + BOOL canSubmitForTextDetection = timePassedSinceSubmittingForText > 0.5 && _finishedReadingText && self.canReadText && [self.textDetector isRealDetector]; + BOOL canSubmitForFaceDetection = timePassedSinceSubmittingForFace > 0.5 && _finishedDetectingFace && self.canDetectFaces && [self.faceDetector isRealDetector]; + BOOL canSubmitForBarcodeDetection = self.canDetectBarcodes && [self.barcodeDetector isRealDetector]; + if (canSubmitForFaceDetection || canSubmitForTextDetection || canSubmitForBarcodeDetection) { + CGSize previewSize = CGSizeMake(_previewLayer.frame.size.width, _previewLayer.frame.size.height); + NSInteger position = self.videoCaptureDeviceInput.device.position; + UIImage *image = [RNCameraUtils convertBufferToUIImage:sampleBuffer previewSize:previewSize position:position]; + // take care of the fact that preview dimensions differ from the ones of the image that we submit for text detection + float scaleX = _previewLayer.frame.size.width / image.size.width; + float scaleY = _previewLayer.frame.size.height / image.size.height; + + // find text features + if (canSubmitForTextDetection) { + _finishedReadingText = false; + self.startText = [NSDate date]; + [self.textDetector findTextBlocksInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * textBlocks) { + NSDictionary *eventText = @{@"type" : @"TextBlock", @"textBlocks" : textBlocks}; + [self onText:eventText]; + self.finishedReadingText = true; + }]; + } + // find face features + if (canSubmitForFaceDetection) { + _finishedDetectingFace = false; + self.startFace = [NSDate date]; + [self.faceDetector findFacesInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * faces) { + NSDictionary *eventFace = @{@"type" : @"face", @"faces" : faces}; + [self onFacesDetected:eventFace]; + self.finishedDetectingFace = true; + }]; + } + // find barcodes + if (canSubmitForBarcodeDetection) { + // Check for the barcode detection mode (Normal, Alternate, Inverted) + switch ([self.barcodeDetector fetchDetectionMode]) { + case RNCameraGoogleVisionBarcodeModeNormal: + self.invertImageData = false; + break; + case RNCameraGoogleVisionBarcodeModeAlternate: + self.invertImageData = !self.invertImageData; + break; + case RNCameraGoogleVisionBarcodeModeInverted: + self.invertImageData = true; + break; + default: + self.invertImageData = false; + break; + } + + if (self.invertImageData) { + image = [RNImageUtils invertColors:image]; + } + + [self.barcodeDetector findBarcodesInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * barcodes) { + NSDictionary *eventBarcode = @{@"type" : @"barcode", @"barcodes" : barcodes}; + [self onBarcodesDetected:eventBarcode]; + }]; + } + } +} + +- (bool)isRecording { + return self.movieFileOutput != nil ? self.movieFileOutput.isRecording : NO; +} + +@end diff --git a/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/android/build.gradle b/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/android/build.gradle new file mode 100644 index 0000000000..71f5227f18 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/android/build.gradle @@ -0,0 +1,81 @@ +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +buildscript { + // The Android Gradle plugin is only required when opening the android folder stand-alone. + // This avoids unnecessary downloads and potential conflicts when the library is included as a + // module dependency in an application project. + if (project == rootProject) { + repositories { + google() + mavenCentral() + jcenter() + } + + dependencies { + //noinspection GradleDependency + classpath("com.android.tools.build:gradle:4.1.0") + } + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 29) + buildToolsVersion safeExtGet('buildToolsVersion', '29.0.2') + + defaultConfig { + multiDexEnabled true + minSdkVersion safeExtGet('minSdkVersion', 16) + targetSdkVersion safeExtGet('targetSdkVersion', 29) + } + + flavorDimensions "react-native-camera" + + productFlavors { + general { + dimension "react-native-camera" + } + mlkit { + dimension "react-native-camera" + } + } + + lintOptions { + abortOnError false + warning 'InvalidPackage' + } + + packagingOptions { + exclude 'META-INF/androidx.exifinterface_exifinterface.version' + exclude 'META-INF/proguard/androidx-annotations.pro' + } +} + +repositories { + google() + jcenter() + mavenCentral() + maven { url "https://jitpack.io" } + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } +} + +dependencies { + //noinspection GradleDynamicVersion + implementation 'com.facebook.react:react-native:+' // From node_modules + implementation "com.google.zxing:core:3.3.3" + implementation "com.drewnoakes:metadata-extractor:2.11.0" + implementation "androidx.exifinterface:exifinterface:1.3.2" + implementation "androidx.annotation:annotation:1.0.0" + implementation "androidx.legacy:legacy-support-v4:1.0.0" + implementation "com.google.android.gms:play-services-mlkit-text-recognition:16.3.0" + generalImplementation "com.google.android.gms:play-services-mlkit-barcode-scanning:16.2.0" + generalImplementation "com.google.android.gms:play-services-mlkit-face-detection:16.2.0" + mlkitImplementation "com.google.mlkit:barcode-scanning:16.2.0" + mlkitImplementation "com.google.mlkit:face-detection:16.1.2" +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/ios/RN/RNCamera.m b/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/ios/RN/RNCamera.m new file mode 100644 index 0000000000..a8943b5223 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-camera/overrides@4.2.1/ios/RN/RNCamera.m @@ -0,0 +1,2327 @@ +#import "RNCamera.h" +#import "RNCameraUtils.h" +#import "RNImageUtils.h" +#import "RNFileSystem.h" +#import +#import +#import +#import +#import +#import "RNSensorOrientationChecker.h" +#import "RNCustomWhiteBalanceSettings.h" + +@interface RNCamera () + +@property (nonatomic, weak) RCTBridge *bridge; +@property (nonatomic,strong) RNSensorOrientationChecker * sensorOrientationChecker; + +@property (nonatomic,strong) UIPinchGestureRecognizer *pinchGestureRecognizer; +@property (nonatomic, strong) RCTPromiseResolveBlock videoRecordedResolve; +@property (nonatomic, strong) RCTPromiseRejectBlock videoRecordedReject; +@property (nonatomic, strong) id textDetector; +@property (nonatomic, strong) id faceDetector; +@property (nonatomic, strong) id barcodeDetector; + +@property (nonatomic, copy) RCTDirectEventBlock onCameraReady; +@property (nonatomic, copy) RCTDirectEventBlock onAudioInterrupted; +@property (nonatomic, copy) RCTDirectEventBlock onAudioConnected; +@property (nonatomic, copy) RCTDirectEventBlock onMountError; +@property (nonatomic, copy) RCTDirectEventBlock onBarCodeRead; +@property (nonatomic, copy) RCTDirectEventBlock onTouch; +@property (nonatomic, copy) RCTDirectEventBlock onTextRecognized; +@property (nonatomic, copy) RCTDirectEventBlock onFacesDetected; +@property (nonatomic, copy) RCTDirectEventBlock onGoogleVisionBarcodesDetected; +@property (nonatomic, copy) RCTDirectEventBlock onPictureTaken; +@property (nonatomic, copy) RCTDirectEventBlock onPictureSaved; +@property (nonatomic, copy) RCTDirectEventBlock onRecordingStart; +@property (nonatomic, copy) RCTDirectEventBlock onRecordingEnd; +@property (nonatomic, assign) BOOL finishedReadingText; +@property (nonatomic, assign) BOOL finishedDetectingFace; +@property (nonatomic, copy) NSDate *startText; +@property (nonatomic, copy) NSDate *startFace; + +@property (nonatomic, copy) RCTDirectEventBlock onSubjectAreaChanged; +@property (nonatomic, assign) BOOL isFocusedOnPoint; +@property (nonatomic, assign) BOOL isExposedOnPoint; +@property (nonatomic, assign) BOOL invertImageData; + +@end + +@implementation RNCamera + +static NSDictionary *defaultFaceDetectorOptions = nil; + +BOOL _recordRequested = NO; +BOOL _sessionInterrupted = NO; + + +- (id)initWithBridge:(RCTBridge *)bridge +{ + if ((self = [super init])) { + self.bridge = bridge; + self.session = [AVCaptureSession new]; + self.sessionQueue = dispatch_queue_create("cameraQueue", DISPATCH_QUEUE_SERIAL); + self.sensorOrientationChecker = [RNSensorOrientationChecker new]; + self.textDetector = [self createTextDetector]; + self.faceDetector = [self createFaceDetectorMlKit]; + self.barcodeDetector = [self createBarcodeDetectorMlKit]; + self.finishedReadingText = true; + self.finishedDetectingFace = true; + self.startText = [NSDate date]; + self.startFace = [NSDate date]; +#if !(TARGET_IPHONE_SIMULATOR) + self.previewLayer = + [AVCaptureVideoPreviewLayer layerWithSession:self.session]; + self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + self.previewLayer.needsDisplayOnBoundsChange = YES; +#endif + self.rectOfInterest = CGRectMake(0, 0, 1.0, 1.0); + + UITapGestureRecognizer * tapHandler=[self createTapGestureRecognizer]; + [self addGestureRecognizer:tapHandler]; + UITapGestureRecognizer * doubleTapHandler=[self createDoubleTapGestureRecognizer]; + [self addGestureRecognizer:doubleTapHandler]; + + self.autoFocus = -1; + self.exposure = -1; + self.presetCamera = AVCaptureDevicePositionUnspecified; + self.cameraId = @""; + self.isFocusedOnPoint = NO; + self.isExposedOnPoint = NO; + self.invertImageData = true; + _recordRequested = NO; + _sessionInterrupted = NO; + + // we will do other initialization after + // the view is loaded. + // This is to prevent code if the view is unused as react + // might create multiple instances of it. + // and we need to also add/remove event listeners. + + + } + return self; +} +-(UITapGestureRecognizer*)createDoubleTapGestureRecognizer +{ + UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; + doubleTapGestureRecognizer.numberOfTapsRequired = 2; + return doubleTapGestureRecognizer; + +} +-(UITapGestureRecognizer*)createTapGestureRecognizer +{ + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; + tapGestureRecognizer.numberOfTapsRequired = 1; + return tapGestureRecognizer; + +} +-(void)handleDoubleTap:(UITapGestureRecognizer*)doubleTapRecognizer { + [self handleTouch:doubleTapRecognizer isDoubleTap:true]; +} +-(void)handleTap:(UITapGestureRecognizer*)tapRecognizer { + [self handleTouch:tapRecognizer isDoubleTap:false]; +} +-(void)handleTouch:(UITapGestureRecognizer*)tapRecognizer isDoubleTap:(BOOL)isDoubleTap{ + if (tapRecognizer.state == UIGestureRecognizerStateRecognized) { + CGPoint location = [tapRecognizer locationInView:self]; + NSDictionary *tapEvent = [NSMutableDictionary dictionaryWithDictionary:@{ + @"isDoubleTap":@(isDoubleTap), + @"touchOrigin": @{ + @"x": @(location.x), + @"y": @(location.y) + } + }]; + [self onTouch:tapEvent]; + } +} +-(float) getMaxZoomFactor:(AVCaptureDevice*)device { + float maxZoom; + if(self.maxZoom > 1){ + maxZoom = MIN(self.maxZoom, device.activeFormat.videoMaxZoomFactor); + }else{ + maxZoom = device.activeFormat.videoMaxZoomFactor; + } + return maxZoom; +} + +-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer { + const CGFloat pinchVelocityDividerFactor = 5.0f; + + if (pinchRecognizer.state == UIGestureRecognizerStateChanged) { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if(device == nil){ + return; + } + NSError *error = nil; + float maxZoom = [self getMaxZoomFactor:device]; + if ([device lockForConfiguration:&error]) { + CGFloat desiredZoomFactor = device.videoZoomFactor + atan2f(pinchRecognizer.velocity, pinchVelocityDividerFactor); + // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor + device.videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, maxZoom)); + [device unlockForConfiguration]; + } else { + NSLog(@"error: %@", error); + } + } +} + +- (void)onReady:(NSDictionary *)event +{ + if (_onCameraReady) { + _onCameraReady(nil); + } +} + +- (void)onMountingError:(NSDictionary *)event +{ + if (_onMountError) { + _onMountError(event); + } +} + +- (void)onCodeRead:(NSDictionary *)event +{ + if (_onBarCodeRead) { + _onBarCodeRead(event); + } +} + +- (void)onPictureTaken:(NSDictionary *)event +{ + if (_onPictureTaken) { + _onPictureTaken(event); + } +} + +- (void)onPictureSaved:(NSDictionary *)event +{ + if (_onPictureSaved) { + _onPictureSaved(event); + } +} + +- (void)onRecordingStart:(NSDictionary *)event +{ + if (_onRecordingStart) { + _onRecordingStart(event); + } +} + +- (void)onRecordingEnd:(NSDictionary *)event +{ + if (_onRecordingEnd) { + _onRecordingEnd(event); + } +} +- (void)onTouch:(NSDictionary *)event +{ + if (_onTouch) { + _onTouch(event); + } +} + +- (void)onText:(NSDictionary *)event +{ + if (_onTextRecognized && _session) { + _onTextRecognized(event); + } +} + +- (void)onSubjectAreaChanged:(NSDictionary *)event +{ + if (_onSubjectAreaChanged) { + _onSubjectAreaChanged(event); + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + self.previewLayer.frame = self.bounds; + [self setBackgroundColor:[UIColor blackColor]]; + [self.layer insertSublayer:self.previewLayer atIndex:0]; +} + +- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex +{ + [self insertSubview:view atIndex:atIndex + 1]; // is this + 1 really necessary? + [super insertReactSubview:view atIndex:atIndex]; + return; +} + +- (void)removeReactSubview:(UIView *)subview +{ + [subview removeFromSuperview]; + [super removeReactSubview:subview]; + return; +} + + +- (void)willMoveToSuperview:(nullable UIView *)newSuperview; +{ + if(newSuperview != nil){ + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(orientationChanged:) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(audioDidInterrupted:) + name:AVAudioSessionInterruptionNotification + object:[AVAudioSession sharedInstance]]; + + + // this is not needed since RN will update our type value + // after mount to set the camera's default, and that will already + // this method + // [self initializeCaptureSessionInput]; + [self.sensorOrientationChecker start]; + [self startSession]; + } + else{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionWasInterruptedNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:self.session]; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + + [self.sensorOrientationChecker stop]; + [self stopSession]; + } + + [super willMoveToSuperview:newSuperview]; +} + + + +// Helper to get a device from the currently set properties (type and camera id) +// might return nil if device failed to be retrieved or is invalid +-(AVCaptureDevice*)getDevice +{ + AVCaptureDevice *captureDevice; + if(self.cameraId != nil && self.cameraId.length){ + captureDevice = [RNCameraUtils deviceWithCameraId:self.cameraId]; + } + else{ + captureDevice = [RNCameraUtils deviceWithMediaType:AVMediaTypeVideo preferringPosition:self.presetCamera]; + } + return captureDevice; + +} + +// helper to return the camera's instance default preset +// this is for pictures only, and video should set another preset +// before recording. +// This default preset returns much smoother photos than High. +-(AVCaptureSessionPreset)getDefaultPreset +{ + AVCaptureSessionPreset preset = + ([self pictureSize] && [[self pictureSize] integerValue] >= 0) ? [self pictureSize] : AVCaptureSessionPresetPhoto; + + return preset; +} + +// helper to return the camera's default preset for videos +// considering what is currently set +-(AVCaptureSessionPreset)getDefaultPresetVideo +{ + // Default video quality AVCaptureSessionPresetHigh if non is provided + AVCaptureSessionPreset preset = + ([self defaultVideoQuality]) ? [RNCameraUtils captureSessionPresetForVideoResolution:[[self defaultVideoQuality] integerValue]] : AVCaptureSessionPresetHigh; + + return preset; +} + +- (void)lockDevice:(AVCaptureDevice *)device andApplySettings:(void (^)(void))applySettings { + NSError *error = nil; + + if(device == nil){ + return; + } + + if (![device lockForConfiguration:&error]) { + if (error) { + RCTLogError(@"%s: %@", __func__, error); + } + return; +} + + applySettings(); + + [device unlockForConfiguration]; +} + +-(void)updateType +{ + [self initializeCaptureSessionInput]; + [self startSession]; // will already check if session is running +} + + +- (void)updateFlashMode +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if(device == nil) { + return; + } + + if (self.flashMode == RNCameraFlashModeTorch) { + if (![device hasTorch] || ![device isTorchModeSupported:AVCaptureTorchModeOn]) { + RCTLogWarn(@"%s: device doesn't support torch mode", __func__); + return; + } + [self lockDevice:device andApplySettings:^{ + [device setFlashMode:AVCaptureFlashModeOff]; + [device setTorchMode:AVCaptureTorchModeOn]; + }]; + } else { + if (![device hasFlash] || ![device isFlashModeSupported:self.flashMode]) { + RCTLogWarn(@"%s: device doesn't support flash mode", __func__); + return; + } + + [self lockDevice:device andApplySettings:^{ + if ([device isTorchActive]) { + [device setTorchMode:AVCaptureTorchModeOff]; + } + [device setFlashMode:self.flashMode]; + }]; + } +} + +// Function to cleanup focus listeners and variables on device +// change. This is required since "defocusing" might not be +// possible on the new device, and our device reference will be +// different +- (void)cleanupFocus:(AVCaptureDevice*) previousDevice { + + self.isFocusedOnPoint = NO; + self.isExposedOnPoint = NO; + + // cleanup listeners if we had any + if(previousDevice != nil){ + + // remove event listener + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:previousDevice]; + + // cleanup device flags + [self lockDevice:previousDevice andApplySettings:^{ + previousDevice.subjectAreaChangeMonitoringEnabled = NO; + }]; + } +} + +- (void)defocusPointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + + + if (self.isFocusedOnPoint) { + + self.isFocusedOnPoint = NO; + + if(device == nil){ + return; + } + + device.subjectAreaChangeMonitoringEnabled = NO; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:device]; + + CGPoint prevPoint = [device focusPointOfInterest]; + + CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f); + + [device setFocusPointOfInterest: autofocusPoint]; + + [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + + [self onSubjectAreaChanged:@{ + @"prevPointOfInterest": @{ + @"x": @(prevPoint.x), + @"y": @(prevPoint.y) + } + }]; + } + + if(self.isExposedOnPoint){ + self.isExposedOnPoint = NO; + + if(device == nil){ + return; + } + + CGPoint exposurePoint = CGPointMake(0.5f, 0.5f); + + [device setExposurePointOfInterest: exposurePoint]; + + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } +} + +- (void)deexposePointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + + + if(self.isExposedOnPoint){ + self.isExposedOnPoint = NO; + + if(device == nil){ + return; + } + + CGPoint exposurePoint = CGPointMake(0.5f, 0.5f); + + [device setExposurePointOfInterest: exposurePoint]; + + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } +} + + +- (void)updateAutoFocusPointOfInterest +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if ([self.autoFocusPointOfInterest objectForKey:@"x"] && [self.autoFocusPointOfInterest objectForKey:@"y"]) { + + float xValue = [self.autoFocusPointOfInterest[@"x"] floatValue]; + float yValue = [self.autoFocusPointOfInterest[@"y"] floatValue]; + + CGPoint autofocusPoint = CGPointMake(xValue, yValue); + + if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + + [device setFocusPointOfInterest:autofocusPoint]; + [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + + if (!self.isFocusedOnPoint) { + self.isFocusedOnPoint = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(autofocusDelegate:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:device]; + device.subjectAreaChangeMonitoringEnabled = YES; + } + } else { + RCTLogWarn(@"AutoFocusPointOfInterest not supported"); + } + + if([self.autoFocusPointOfInterest objectForKey:@"autoExposure"]){ + BOOL autoExposure = [self.autoFocusPointOfInterest[@"autoExposure"] boolValue]; + + if(autoExposure){ + if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) + { + [device setExposurePointOfInterest:autofocusPoint]; + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + self.isExposedOnPoint = YES; + + } else { + RCTLogWarn(@"AutoExposurePointOfInterest not supported"); + } + } + else{ + [self deexposePointOfInterest]; + } + } + else{ + [self deexposePointOfInterest]; + } + + } else { + [self defocusPointOfInterest]; + [self deexposePointOfInterest]; + } + }]; +} + +- (void)autofocusDelegate:(NSNotification*) notification { + AVCaptureDevice* device = [notification object]; + + [self lockDevice:device andApplySettings:^{ + [self defocusPointOfInterest]; + [self deexposePointOfInterest]; + }]; +} + +- (void)updateFocusMode +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if ([device isFocusModeSupported:self.autoFocus]) { + [self lockDevice:device andApplySettings:^{ + [device setFocusMode:self.autoFocus]; + }]; + } +} + +- (void)updateFocusDepth +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + if (device == nil || self.autoFocus < 0 || device.focusMode != RNCameraAutoFocusOff || device.position == RNCameraTypeFront) { + return; + } + + if (![device respondsToSelector:@selector(isLockingFocusWithCustomLensPositionSupported)] || ![device isLockingFocusWithCustomLensPositionSupported]) { + RCTLogWarn(@"%s: Setting focusDepth isn't supported for this camera device", __func__); + return; + } + + [self lockDevice:device andApplySettings:^{ + [device setFocusModeLockedWithLensPosition:self.focusDepth completionHandler:nil]; + }]; +} + +- (void)updateZoom { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + float maxZoom = [self getMaxZoomFactor:device]; + device.videoZoomFactor = (maxZoom - 1) * self.zoom + 1; + }]; +} + +- (void)updateWhiteBalance { + if (self.customWhiteBalanceSettings != nil) { + [self applyCustomWhiteBalance]; + } else { + [self applyDefaultWhiteBalance]; + } +} + +- (void)applyDefaultWhiteBalance { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if (self.whiteBalance == RNCameraWhiteBalanceAuto || ![device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { + if (self.whiteBalance != RNCameraWhiteBalanceAuto) { + RCTLogWarn(@"%s: locked whitebalance mode ist note supported. Fallback to continuous auto white balance mode", __func__); + } +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]; +#endif + } else { + AVCaptureWhiteBalanceTemperatureAndTintValues temperatureAndTint = { + .temperature = [RNCameraUtils temperatureForWhiteBalance:self.whiteBalance], + .tint = 0, + }; + AVCaptureWhiteBalanceGains rgbGains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:temperatureAndTint]; + + @try{ +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:rgbGains completionHandler:nil]; +#endif + } + @catch(NSException *exception){ + RCTLogError(@"Failed to set white balance: %@", exception); + } + } + }]; +} + +- (void)applyCustomWhiteBalance { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + if (![device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { + RCTLogWarn(@"%s: locked whitebalance mode ist note supported. Fallback to continuous auto white balance mode", __func__); +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]; +#endif + } else { + AVCaptureWhiteBalanceTemperatureAndTintValues temperatureAndTint = { + .temperature = self.customWhiteBalanceSettings.temperature, + .tint = self.customWhiteBalanceSettings.tint, + }; + AVCaptureWhiteBalanceGains rgbGains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:temperatureAndTint]; + CGFloat redGain = rgbGains.redGain + self.customWhiteBalanceSettings.redGainOffset; + CGFloat greenGain = rgbGains.greenGain + self.customWhiteBalanceSettings.greenGainOffset; + CGFloat blueGain = rgbGains.blueGain + self.customWhiteBalanceSettings.blueGainOffset; + + rgbGains.redGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, redGain)); + rgbGains.greenGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, greenGain)); + rgbGains.blueGain = MAX(1.0f, MIN(device.maxWhiteBalanceGain, blueGain)); + + @try{ +#if BUILD_PLATFORM==macos +#else + [device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:rgbGains completionHandler:nil]; + +#endif + } @catch(NSException *exception){ + RCTLogError(@"Failed to set custom white balance: %@", exception); + } + } + }]; +} + + +/// Set the AVCaptureDevice's ISO values based on RNCamera's 'exposure' value, +/// which is a float between 0 and 1 if defined by the user or -1 to indicate that no +/// selection is active. 'exposure' gets mapped to a valid ISO value between the +/// device's min/max-range of ISO-values. +/// +/// The exposure gets reset every time the user manually sets the autofocus-point in +/// 'updateAutoFocusPointOfInterest' automatically. Currently no explicit event is fired. +/// This leads to two 'exposure'-states: one here and one in the component, which is +/// fine. 'exposure' here gets only synced if 'exposure' on the js-side changes. You +/// can manually keep the state in sync by setting 'exposure' in your React-state +/// everytime the js-updateAutoFocusPointOfInterest-function gets called. +- (void)updateExposure +{ + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + [self lockDevice:device andApplySettings:^{ + // Check that either no explicit exposure-val has been set yet + // or that it has been reset. Check for > 1 is only a guard. + if(self.exposure < 0 || self.exposure > 1){ + [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + return; + } + + // Lazy init of range. + if(!self.exposureIsoMin){ self.exposureIsoMin = device.activeFormat.minISO; } + if(!self.exposureIsoMax){ self.exposureIsoMax = device.activeFormat.maxISO; } + + // Get a valid ISO-value in range from min to max. After we mapped the exposure + // (a val between 0 - 1), the result gets corrected by the offset from 0, which + // is the min-ISO-value. + float appliedExposure = (self.exposureIsoMax - self.exposureIsoMin) * self.exposure + self.exposureIsoMin; + + // Make sure we're in AVCaptureExposureModeCustom, else the ISO + duration time won't apply. + // Also make sure the device can set exposure + if([device isExposureModeSupported:AVCaptureExposureModeCustom]){ + if(device.exposureMode != AVCaptureExposureModeCustom){ + [device setExposureMode:AVCaptureExposureModeCustom]; + } + + // Only set the ISO for now, duration will be default as a change might affect frame rate. + @try{ + [device setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent ISO:appliedExposure completionHandler:nil]; + } + @catch(NSException *exception){ + RCTLogError(@"Failed to update exposure: %@", exception); + } + + } else { + RCTLog(@"Device does not support AVCaptureExposureModeCustom"); + } + }]; +} + +- (void)updatePictureSize +{ + // make sure to call this function so the right default is used if + // "None" is used + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + + +- (void)updateCaptureAudio +{ + dispatch_async(self.sessionQueue, ^{ + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + else{ + [self removeAudioCaptureSessionInput]; + } + }); +} + +- (void)takePictureWithOrientation:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject{ + + UIInterfaceOrientation orientation = [self.sensorOrientationChecker getDeviceOrientation]; + + NSMutableDictionary *tmpOptions = [options mutableCopy]; + + if ([tmpOptions valueForKey:@"orientation"] == nil) { + tmpOptions[@"orientation"] = [NSNumber numberWithInteger:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation:orientation]]; + } + self.deviceOrientation = [NSNumber numberWithInteger:orientation]; + self.orientation = [NSNumber numberWithInteger:[tmpOptions[@"orientation"] integerValue]]; + [self takePicture:tmpOptions resolve:resolve reject:reject]; +} + +- (void)takePicture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject +{ + // if video device is not set, reject + if(self.videoCaptureDeviceInput == nil || !self.session.isRunning){ + reject(@"E_IMAGE_CAPTURE_FAILED", @"Camera is not ready.", nil); + return; + } + + // make sure to get orientation info here + // as it may change if multiple consecutive calls are done + NSInteger orientation; + NSNumber* deviceOrientation; + + @synchronized (self) { + if (!self.deviceOrientation) { + [self takePictureWithOrientation:options resolve:resolve reject:reject]; + return; + } + + orientation = [options[@"orientation"] integerValue]; + deviceOrientation = self.deviceOrientation; + + self.orientation = nil; + self.deviceOrientation = nil; + } + + AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; + [connection setVideoOrientation:orientation]; + @try { + [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { + if (imageSampleBuffer && !error) { + + if ([options[@"pauseAfterCapture"] boolValue]) { + [[self.previewLayer connection] setEnabled:NO]; + } + + BOOL useFastMode = [options valueForKey:@"fastMode"] != nil && [options[@"fastMode"] boolValue]; + if (useFastMode) { + resolve(nil); + } + + [self onPictureTaken:@{}]; + + + // get JPEG image data + NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; + UIImage *takenImage = [UIImage imageWithData:imageData]; + + + // Adjust/crop image based on preview dimensions + // TODO: This seems needed because iOS does not allow + // for aspect ratio settings, so this is the best we can get + // to mimic android's behaviour. + CGImageRef takenCGImage = takenImage.CGImage; + CGSize previewSize; + if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) { + previewSize = CGSizeMake(self.previewLayer.frame.size.height, self.previewLayer.frame.size.width); + } else { + previewSize = CGSizeMake(self.previewLayer.frame.size.width, self.previewLayer.frame.size.height); + } + CGRect cropRect = CGRectMake(0, 0, CGImageGetWidth(takenCGImage), CGImageGetHeight(takenCGImage)); + CGRect croppedSize = AVMakeRectWithAspectRatioInsideRect(previewSize, cropRect); + takenImage = [RNImageUtils cropImage:takenImage toRect:croppedSize]; + + // apply other image settings + bool resetOrientation = NO; + if ([options[@"mirrorImage"] boolValue]) { + takenImage = [RNImageUtils mirrorImage:takenImage]; + } + if ([options[@"forceUpOrientation"] boolValue]) { + takenImage = [RNImageUtils forceUpOrientation:takenImage]; + resetOrientation = YES; + } + if ([options[@"width"] integerValue]) { + takenImage = [RNImageUtils scaleImage:takenImage toWidth:[options[@"width"] integerValue]]; + resetOrientation = YES; + } + + // get image metadata so we can re-add it later + // make it mutable since we need to adjust quality/compression + CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); + + CFMutableDictionaryRef mutableMetaDict = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); + + // release the meta dict now that we've copied it + // to Objective-C land + CFRelease(metaDict); + + // bridge the copy for auto release + NSMutableDictionary *metadata = (NSMutableDictionary *)CFBridgingRelease(mutableMetaDict); + + RNCameraImageType imageType = RNCameraImageTypeJPEG; + CFStringRef imageTypeIdentifier = kUTTypeJPEG; + NSString *imageExtension = @".jpg"; + if ([options[@"imageType"] isEqualToString:@"png"]) { + imageType = RNCameraImageTypePNG; + imageTypeIdentifier = kUTTypePNG; + imageExtension = @".png"; + } + + // Get final JPEG image and set compression + NSString *qualityKey = (__bridge NSString *)kCGImageDestinationLossyCompressionQuality; + if (imageType == RNCameraImageTypeJPEG) { + float quality = [options[@"quality"] floatValue]; + [metadata setObject:@(quality) forKey:qualityKey]; + } + + + // Reset exif orientation if we need to due to image changes + // that already rotate the image. + // Other dimension attributes will be set automatically + // regardless of what we have on our metadata dict + if (resetOrientation){ + metadata[(NSString*)kCGImagePropertyOrientation] = @(1); + } + + + // get our final image data with added metadata + // idea taken from: https://stackoverflow.com/questions/9006759/how-to-write-exif-metadata-to-an-image-not-the-camera-roll-just-a-uiimage-or-j/9091472 + NSMutableData * destData = [NSMutableData data]; + + CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destData, imageTypeIdentifier, 1, NULL); + + // defaults to true, must like Android + bool writeExif = true; + + if(options[@"writeExif"]){ + + // if we received an object, merge with our meta + if ([options[@"writeExif"] isKindOfClass:[NSDictionary class]]){ + NSDictionary *newExif = options[@"writeExif"]; + + // need to update both, since apple splits data + // across exif and tiff dicts. No problems with duplicates + // they will be handled appropiately. + NSMutableDictionary *exif = metadata[(NSString*)kCGImagePropertyExifDictionary]; + + NSMutableDictionary *tiff = metadata[(NSString*)kCGImagePropertyTIFFDictionary]; + + + // initialize exif dict if not built + if(!exif){ + exif = [[NSMutableDictionary alloc] init]; + metadata[(NSString*)kCGImagePropertyExifDictionary] = exif; + } + + if(!tiff){ + tiff = [[NSMutableDictionary alloc] init]; + metadata[(NSString*)kCGImagePropertyTIFFDictionary] = exif; + } + + // merge new exif info + [exif addEntriesFromDictionary:newExif]; + [tiff addEntriesFromDictionary:newExif]; + + + // correct any GPS metadata like Android does + // need to get the right format for each value. + NSMutableDictionary *gpsDict = [[NSMutableDictionary alloc] init]; + + if(newExif[@"GPSLatitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSLatitude] = @(fabs([newExif[@"GPSLatitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSLatitudeRef] = [newExif[@"GPSLatitude"] floatValue] >= 0 ? @"N" : @"S"; + + } + if(newExif[@"GPSLongitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSLongitude] = @(fabs([newExif[@"GPSLongitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSLongitudeRef] = [newExif[@"GPSLongitude"] floatValue] >= 0 ? @"E" : @"W"; + } + if(newExif[@"GPSAltitude"]){ + gpsDict[(NSString *)kCGImagePropertyGPSAltitude] = @(fabs([newExif[@"GPSAltitude"] floatValue])); + + gpsDict[(NSString *)kCGImagePropertyGPSAltitudeRef] = [newExif[@"GPSAltitude"] floatValue] >= 0 ? @(0) : @(1); + } + + // if we don't have gps info, add it + // otherwise, merge it + if(!metadata[(NSString *)kCGImagePropertyGPSDictionary]){ + metadata[(NSString *)kCGImagePropertyGPSDictionary] = gpsDict; + } + else{ + [metadata[(NSString *)kCGImagePropertyGPSDictionary] addEntriesFromDictionary:gpsDict]; + } + + } + else{ + writeExif = [options[@"writeExif"] boolValue]; + } + + } + + CFDictionaryRef finalMetaData = nil; + if (writeExif) { + finalMetaData = (__bridge CFDictionaryRef)metadata; + } else if (metadata[qualityKey]) { + // In order to apply the desired compression quality, + // it is necessary to specify the kCGImageDestinationLossyCompressionQuality in the metadata. + finalMetaData = (__bridge CFDictionaryRef)@{qualityKey: metadata[qualityKey]}; + } + + CGImageDestinationAddImage(destination, takenImage.CGImage, finalMetaData); + + + // write final image data with metadata to our destination + if (CGImageDestinationFinalize(destination)){ + + NSMutableDictionary *response = [[NSMutableDictionary alloc] init]; + + NSString *path = nil; + if (options[@"path"]) { + path = options[@"path"]; + } + else{ + path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:imageExtension]; + } + + bool success = YES; + + if (![options[@"doNotSave"] boolValue]) { + NSString* pathRes = [RNImageUtils writeImage:destData toPath:path]; + if (!pathRes) { + reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be saved: file write failed.", nil); + success = NO; + } else { + response[@"uri"] = pathRes; + } + + } + + if (success) { + response[@"width"] = @(takenImage.size.width); + response[@"height"] = @(takenImage.size.height); + + if ([options[@"base64"] boolValue]) { + response[@"base64"] = [destData base64EncodedStringWithOptions:0]; + } + + if ([options[@"exif"] boolValue]) { + response[@"exif"] = metadata; + + // No longer needed since we always get the photo metadata now + //[RNImageUtils updatePhotoMetadata:imageSampleBuffer withAdditionalData:@{ @"Orientation": @(imageRotation) } inResponse:response]; // TODO + } + + response[@"pictureOrientation"] = @(orientation); + response[@"deviceOrientation"] = @([deviceOrientation integerValue]); + + if (useFastMode) { + [self onPictureSaved:@{@"data": response, @"id": options[@"id"]}]; + } else { + resolve(response); + } + } + } + else{ + reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be saved", error); + } + + // release image resource + @try{ + CFRelease(destination); + } + @catch(NSException *exception){ + RCTLogError(@"Failed to release CGImageDestinationRef: %@", exception); + } + + } else { + reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be captured", error); + } + }]; + } @catch (NSException *exception) { + reject( + @"E_IMAGE_CAPTURE_FAILED", + @"Got exception while taking picture", + [NSError errorWithDomain:@"E_IMAGE_CAPTURE_FAILED" code: 500 userInfo:@{NSLocalizedDescriptionKey:exception.reason}] + ); + } +} + +- (void)recordWithOrientation:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject{ + + UIInterfaceOrientation orientation = [self.sensorOrientationChecker getDeviceOrientation]; + NSMutableDictionary *tmpOptions = [options mutableCopy]; + if ([tmpOptions valueForKey:@"orientation"] == nil) { + tmpOptions[@"orientation"] = [NSNumber numberWithInteger:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation: orientation]]; + } + self.deviceOrientation = [NSNumber numberWithInteger:orientation]; + self.orientation = [NSNumber numberWithInteger:[tmpOptions[@"orientation"] integerValue]]; + [self record:tmpOptions resolve:resolve reject:reject]; +} + +- (void)record:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject +{ + if(self.videoCaptureDeviceInput == nil || !self.session.isRunning){ + reject(@"E_VIDEO_CAPTURE_FAILED", @"Camera is not ready.", nil); + return; + } + + if (!self.deviceOrientation) { + [self recordWithOrientation:options resolve:resolve reject:reject]; + return; + } + + NSInteger orientation = [options[@"orientation"] integerValue]; + + // some operations will change our config + // so we batch config updates, even if inner calls + // might also call this, only the outermost commit will take effect + // making the camera changes much faster. + [self.session beginConfiguration]; + + + if (_movieFileOutput == nil) { + // At the time of writing AVCaptureMovieFileOutput and AVCaptureVideoDataOutput (> GMVDataOutput) + // cannot coexist on the same AVSession (see: https://stackoverflow.com/a/4986032/1123156). + // We stop face detection here and restart it in when AVCaptureMovieFileOutput finishes recording. + if ([self.textDetector isRealDetector]) { + [self stopTextRecognition]; + } + if ([self.faceDetector isRealDetector]) { + [self stopFaceDetection]; + } + if ([self.barcodeDetector isRealDetector]) { + [self stopBarcodeDetection]; + } + [self setupMovieFileCapture]; + } + + if (self.movieFileOutput == nil || self.movieFileOutput.isRecording || _videoRecordedResolve != nil || _videoRecordedReject != nil) { + [self.session commitConfiguration]; + return; + } + + + // video preset will be cleanedup/restarted once capture is done + // with a camera cleanup call + if (options[@"quality"]) { + AVCaptureSessionPreset newQuality = [RNCameraUtils captureSessionPresetForVideoResolution:(RNCameraVideoResolution)[options[@"quality"] integerValue]]; + if (self.session.sessionPreset != newQuality) { + [self updateSessionPreset:newQuality]; + } + } + else{ + AVCaptureSessionPreset newQuality = [self getDefaultPresetVideo]; + if (self.session.sessionPreset != newQuality) { + [self updateSessionPreset:newQuality]; + } + } + + AVCaptureConnection *connection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; + + if (self.videoStabilizationMode != 0) { + if (connection.isVideoStabilizationSupported == NO) { + RCTLogWarn(@"%s: Video Stabilization is not supported on this device.", __func__); + } else { + [connection setPreferredVideoStabilizationMode:self.videoStabilizationMode]; + } + } + [connection setVideoOrientation:orientation]; + + BOOL recordAudio = [options valueForKey:@"mute"] == nil || ([options valueForKey:@"mute"] != nil && ![options[@"mute"] boolValue]); + + // sound recording connection, we can easily turn it on/off without manipulating inputs, this prevents flickering. + // note that mute will also be set to true + // if captureAudio is set to false on the JS side. + // Check the property anyways just in case it is manipulated + // with setNativeProps + if(recordAudio && self.captureAudio){ + + // if we haven't initialized our capture session yet + // initialize it (this will cause video to flicker.) + // Dispatch through our session queue as we may have race conditions + // with this and the captureAudio prop + dispatch_async(self.sessionQueue, ^{ + [self initializeAudioCaptureSessionInput]; + // finally, make sure we got access to the capture device + // and turn the connection on. + if(self.audioCaptureDeviceInput != nil){ + AVCaptureConnection *audioConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeAudio]; + audioConnection.enabled = YES; + } + }); + } + + // if we have a capture input but are muted + // disable connection. No flickering here. + else if(self.audioCaptureDeviceInput != nil){ + AVCaptureConnection *audioConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeAudio]; + audioConnection.enabled = NO; + } + + dispatch_async(self.sessionQueue, ^{ + + // session preset might affect this, so we run this code + // also in the session queue + + if (options[@"maxDuration"]) { + Float64 maxDuration = [options[@"maxDuration"] floatValue]; + self.movieFileOutput.maxRecordedDuration = CMTimeMakeWithSeconds(maxDuration, 30); + } + + if (options[@"maxFileSize"]) { + self.movieFileOutput.maxRecordedFileSize = [options[@"maxFileSize"] integerValue]; + } + + if (options[@"fps"]) { + AVCaptureDevice *device = [self.videoCaptureDeviceInput device]; + AVCaptureDeviceFormat *activeFormat = device.activeFormat; + CMFormatDescriptionRef activeDescription = activeFormat.formatDescription; + CMVideoDimensions activeDimensions = CMVideoFormatDescriptionGetDimensions(activeDescription); + + NSInteger fps = [options[@"fps"] integerValue]; + CGFloat desiredFPS = (CGFloat)fps; + + AVCaptureDeviceFormat *selectedFormat = nil; + int32_t activeWidth = activeDimensions.width; + int32_t activeHeight = activeDimensions.height; + + for (AVCaptureDeviceFormat *format in [device formats]) { + CMFormatDescriptionRef formatDescription = format.formatDescription; + CMVideoDimensions formatDimensions = CMVideoFormatDescriptionGetDimensions(formatDescription); + int32_t formatWidth = formatDimensions.width; + int32_t formatHeight = formatDimensions.height; + if (formatWidth != activeWidth || formatHeight != activeHeight) { + continue; + } + + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { + if (range.minFrameRate <= desiredFPS && desiredFPS <= range.maxFrameRate) { + selectedFormat = format; + } + } + } + + if (selectedFormat) { + if ([device lockForConfiguration:nil]) { + device.activeFormat = selectedFormat; + device.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)desiredFPS); + device.activeVideoMaxFrameDuration = CMTimeMake(1, (int32_t)desiredFPS); + [device unlockForConfiguration]; + } + } else { + RCTLog(@"We could not find a suitable format for this device."); + } + } + + if (options[@"codec"]) { + if (@available(iOS 10, *)) { + AVVideoCodecType videoCodecType = options[@"codec"]; + + if ([self.movieFileOutput.availableVideoCodecTypes containsObject:videoCodecType]) { + self.videoCodecType = videoCodecType; + + BOOL supportsBitRate = NO; + + // prevent crashing due to unsupported keys + if (@available(iOS 12.0, *)) { + supportsBitRate = [[self.movieFileOutput supportedOutputSettingsKeysForConnection:connection] containsObject:AVVideoCompressionPropertiesKey]; + } + + if(options[@"videoBitrate"] && supportsBitRate) { + NSString *videoBitrate = options[@"videoBitrate"]; + [self.movieFileOutput setOutputSettings:@{ + AVVideoCodecKey:videoCodecType, + AVVideoCompressionPropertiesKey: + @{ + AVVideoAverageBitRateKey:videoBitrate + } + } forConnection:connection]; + } else { + [self.movieFileOutput setOutputSettings:@{AVVideoCodecKey:videoCodecType} forConnection:connection]; + } + } else { + RCTLogWarn(@"Video Codec %@ is not available.", videoCodecType); + } + } + else { + RCTLogWarn(@"%s: Setting videoCodec is only supported above iOS version 10.", __func__); + } + } + + NSString *path = nil; + if (options[@"path"]) { + path = options[@"path"]; + } + else { + path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".mov"]; + } + + if ([options[@"mirrorVideo"] boolValue]) { + if ([connection isVideoMirroringSupported]) { + [connection setAutomaticallyAdjustsVideoMirroring:NO]; + [connection setVideoMirrored:YES]; + } + } + + // finally, commit our config changes before starting to record + [self.session commitConfiguration]; + + // and update flash in case it was turned off automatically + // due to session/preset changes + [self updateFlashMode]; + + // after everything is set, start recording with a tiny delay + // to ensure the camera already has focus and exposure set. + double delayInSeconds = 0.5; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); + + // we will use this flag to stop recording + // if it was requested to stop before it could even start + _recordRequested = YES; + + dispatch_after(popTime, self.sessionQueue, ^(void){ + + // our session might have stopped in between the timeout + // so make sure it is still valid, otherwise, error and cleanup + if(self.movieFileOutput != nil && self.videoCaptureDeviceInput != nil && _recordRequested){ + NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:path]; + [self.movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self]; + self.videoRecordedResolve = resolve; + self.videoRecordedReject = reject; + + [self onRecordingStart:@{ + @"uri": outputURL.absoluteString, + @"videoOrientation": @([self.orientation integerValue]), + @"deviceOrientation": @([self.deviceOrientation integerValue]) + }]; + + } + else{ + reject(@"E_VIDEO_CAPTURE_FAILED", !_recordRequested ? @"Recording request cancelled." : @"Camera is not ready.", nil); + [self cleanupCamera]; + } + + // reset our flag + _recordRequested = NO; + }); + + + }); +} + +- (void)stopRecording +{ + dispatch_async(self.sessionQueue, ^{ + if ([self.movieFileOutput isRecording]) { + [self.movieFileOutput stopRecording]; + [self onRecordingEnd:@{}]; + } else { + if(_recordRequested){ + _recordRequested = NO; + } + else{ + RCTLogWarn(@"Video is not recording."); + } + } + }); +} + +- (void)resumePreview +{ + [[self.previewLayer connection] setEnabled:YES]; +} + +- (void)pausePreview +{ + [[self.previewLayer connection] setEnabled:NO]; +} + +- (void)startSession +{ +#if TARGET_IPHONE_SIMULATOR + [self onReady:nil]; + return; +#endif + dispatch_async(self.sessionQueue, ^{ + + // if session already running, also return and fire ready event + // this is helpfu when the device type or ID is changed and we must + // receive another ready event (like Android does) + if(self.session.isRunning){ + [self onReady:nil]; + return; + } + + // if camera not set (invalid type and no ID) return. + if (self.presetCamera == AVCaptureDevicePositionUnspecified && (self.cameraId == nil || !self.cameraId.length)) { + return; + } + + // video device was not initialized, also return + if(self.videoCaptureDeviceInput == nil){ + return; + } + + + AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; + if ([self.session canAddOutput:stillImageOutput]) { + stillImageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG, AVVideoQualityKey: @(1.0)}; + [self.session addOutput:stillImageOutput]; + [stillImageOutput setHighResolutionStillImageOutputEnabled:YES]; + self.stillImageOutput = stillImageOutput; + } + + // If AVCaptureVideoDataOutput is not required because of Google Vision + // (see comment in -record), we go ahead and add the AVCaptureMovieFileOutput + // to avoid an exposure rack on some devices that can cause the first few + // frames of the recorded output to be underexposed. + if (![self.faceDetector isRealDetector] && ![self.textDetector isRealDetector] && ![self.barcodeDetector isRealDetector]) { + [self setupMovieFileCapture]; + } + [self setupOrDisableBarcodeScanner]; + + _sessionInterrupted = NO; + [self.session startRunning]; + [self onReady:nil]; + }); +} + +- (void)stopSession +{ +#if TARGET_IPHONE_SIMULATOR + return; +#endif + dispatch_async(self.sessionQueue, ^{ + if ([self.textDetector isRealDetector]) { + [self stopTextRecognition]; + } + if ([self.faceDetector isRealDetector]) { + [self stopFaceDetection]; + } + if ([self.barcodeDetector isRealDetector]) { + [self stopBarcodeDetection]; + } + [self.previewLayer removeFromSuperlayer]; + [self.session commitConfiguration]; + [self.session stopRunning]; + + for (AVCaptureInput *input in self.session.inputs) { + [self.session removeInput:input]; + } + + for (AVCaptureOutput *output in self.session.outputs) { + [self.session removeOutput:output]; + } + + // cleanup audio input if any, and release + // audio session so other apps can continue playback. + [self removeAudioCaptureSessionInput]; + + // clean these up as well since we've removed + // all inputs and outputs from session + self.videoCaptureDeviceInput = nil; + self.audioCaptureDeviceInput = nil; + self.movieFileOutput = nil; + }); +} + +// Initializes audio capture device +// Note: Ensure this is called within a a session configuration block +- (void)initializeAudioCaptureSessionInput +{ + // only initialize if not initialized already + if(self.audioCaptureDeviceInput == nil){ + NSError *error = nil; + + AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; + AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error]; + + if (error || audioDeviceInput == nil) { + RCTLogWarn(@"%s: %@", __func__, error); + } + + else{ + + // test if we can activate the device input. + // If we fail, means it is already being used + BOOL setActive = [[AVAudioSession sharedInstance] setActive:YES error:&error]; + + if (!setActive) { + RCTLogWarn(@"Audio device could not set active: %s: %@", __func__, error); + } + + else if ([self.session canAddInput:audioDeviceInput]) { + [self.session addInput:audioDeviceInput]; + self.audioCaptureDeviceInput = audioDeviceInput; + + // inform that audio has been resumed + if(self.onAudioConnected){ + self.onAudioConnected(nil); + } + } + else{ + RCTLog(@"Cannot add audio input"); + } + } + + // if we failed to get the audio device, fire our interrupted event + if(self.audioCaptureDeviceInput == nil && self.onAudioInterrupted){ + self.onAudioInterrupted(nil); + } + } +} + + +// Removes audio capture from the session, allowing the session +// to resume if it was interrupted, and stopping any +// recording in progress with the appropriate flags. +- (void)removeAudioCaptureSessionInput +{ + if(self.audioCaptureDeviceInput != nil){ + + BOOL audioRemoved = NO; + + if ([self.session.inputs containsObject:self.audioCaptureDeviceInput]) { + + if ([self isRecording]) { + self.isRecordingInterrupted = YES; + } + + [self.session removeInput:self.audioCaptureDeviceInput]; + + self.audioCaptureDeviceInput = nil; + + // update flash since it gets reset when + // we change the session inputs + dispatch_async(self.sessionQueue, ^{ + [self updateFlashMode]; + }); + + audioRemoved = YES; + } + + // Deactivate our audio session so other audio can resume + // playing, if any. E.g., background music. + // unless told not to + if(!self.keepAudioSession){ + NSError *error = nil; + + BOOL setInactive = [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]; + + if (!setInactive) { + RCTLogWarn(@"Audio device could not set inactive: %s: %@", __func__, error); + } + } + + self.audioCaptureDeviceInput = nil; + + // inform that audio was interrupted + if(audioRemoved && self.onAudioInterrupted){ + self.onAudioInterrupted(nil); + } + } +} + + +- (void)initializeCaptureSessionInput +{ + + dispatch_async(self.sessionQueue, ^{ + + // Do all camera initialization in the session queue + // to prevent it from + AVCaptureDevice *captureDevice = [self getDevice]; + + // if setting a new device is the same we currently have, nothing to do + // return. + if(self.videoCaptureDeviceInput != nil && captureDevice != nil && [self.videoCaptureDeviceInput.device.uniqueID isEqualToString:captureDevice.uniqueID]){ + return; + } + + // if the device we are setting is also invalid/nil, return + if(captureDevice == nil){ + [self onMountingError:@{@"message": @"Invalid camera device."}]; + return; + } + + // get orientation also in our session queue to prevent + // race conditions and also blocking the main thread + __block UIInterfaceOrientation interfaceOrientation; + + dispatch_sync(dispatch_get_main_queue(), ^{ + interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; + }); + + AVCaptureVideoOrientation orientation = [RNCameraUtils videoOrientationForInterfaceOrientation:interfaceOrientation]; + + + [self.session beginConfiguration]; + + NSError *error = nil; + AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; + + if(error != nil){ + NSLog(@"Capture device error %@", error); + } + + if (error || captureDeviceInput == nil) { + RCTLog(@"%s: %@", __func__, error); + [self.session commitConfiguration]; + [self onMountingError:@{@"message": @"Failed to setup capture device."}]; + return; + } + + + // Do additional cleanup that might be needed on the + // previous device, if any. + AVCaptureDevice *previousDevice = self.videoCaptureDeviceInput != nil ? self.videoCaptureDeviceInput.device : nil; + + [self cleanupFocus:previousDevice]; + + + // Remove inputs + [self.session removeInput:self.videoCaptureDeviceInput]; + + // clear this variable before setting it again. + // Otherwise, if setting fails, we end up with a stale value. + // and we are no longer able to detect if it changed or not + self.videoCaptureDeviceInput = nil; + + // setup our capture preset based on what was set from RN + // and our defaults + // if the preset is not supported (e.g., when switching cameras) + // canAddInput below will fail + self.session.sessionPreset = [self getDefaultPreset]; + + + // reset iso cached values, these might be different + // from camera to camera. Otherwise, the camera may crash + // when changing cameras and exposure. + self.exposureIsoMin = 0; + self.exposureIsoMax = 0; + + + if ([self.session canAddInput:captureDeviceInput]) { + [self.session addInput:captureDeviceInput]; + + self.videoCaptureDeviceInput = captureDeviceInput; + + // Update all these async after our session has commited + // since some values might be changed on session commit. + dispatch_async(self.sessionQueue, ^{ + [self updateZoom]; + [self updateFocusMode]; + [self updateFocusDepth]; + [self updateExposure]; + [self updateAutoFocusPointOfInterest]; + [self updateWhiteBalance]; + [self updateFlashMode]; + }); + + [self.previewLayer.connection setVideoOrientation:orientation]; + [self _updateMetadataObjectsToRecognize]; + } + else{ + RCTLog(@"The selected device does not work with the Preset [%@] or configuration provided", self.session.sessionPreset); + + [self onMountingError:@{@"message": @"Camera device does not support selected settings."}]; + } + + + // if we have not yet set our audio capture device, + // set it. Setting it early will prevent flickering when + // recording a video + // Only set it if captureAudio is true so we don't prompt + // for permission if audio is not needed. + // TODO: If we can update checkRecordAudioAuthorizationStatus + // to actually do something in production, we can replace + // the captureAudio prop by a simple permission check; + // for example, checking + // [[AVAudioSession sharedInstance] recordPermission] == AVAudioSessionRecordPermissionGranted + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + + [self.session commitConfiguration]; + }); +} + +#pragma mark - internal + +- (void)updateSessionPreset:(AVCaptureSessionPreset)preset +{ +#if !(TARGET_IPHONE_SIMULATOR) + if ([preset integerValue] < 0) { + return; + } + if (preset) { + if (self.canDetectFaces && [preset isEqual:AVCaptureSessionPresetPhoto]) { + RCTLog(@"AVCaptureSessionPresetPhoto not supported during face detection. Falling back to AVCaptureSessionPresetHigh"); + preset = AVCaptureSessionPresetHigh; + } + dispatch_async(self.sessionQueue, ^{ + if ([self.session canSetSessionPreset:preset]) { + [self.session beginConfiguration]; + self.session.sessionPreset = preset; + [self.session commitConfiguration]; + + // Need to update these since it gets reset on preset change + [self updateFlashMode]; + [self updateZoom]; + } + else{ + RCTLog(@"The selected preset [%@] does not work with the current session.", preset); + } + }); + } +#endif +} + + +// We are using this event to detect audio interruption ended +// events since we won't receive it on our session +// after disabling audio. +- (void)audioDidInterrupted:(NSNotification *)notification +{ + NSDictionary *userInfo = notification.userInfo; + NSInteger type = [[userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + + + // if our audio interruption ended + if(type == AVAudioSessionInterruptionTypeEnded){ + + // and the end event contains a hint that we should resume + // audio. Then re-connect our audio session if we are + // capturing audio. + // Sometimes we are hinted to not resume audio; e.g., + // when playing music in background. + + NSInteger option = [[userInfo valueForKey:AVAudioSessionInterruptionOptionKey] integerValue]; + + if(self.captureAudio && option == AVAudioSessionInterruptionOptionShouldResume){ + + dispatch_async(self.sessionQueue, ^{ + + // initialize audio if we need it + // check again captureAudio in case it was changed + // in between + if(self.captureAudio){ + [self initializeAudioCaptureSessionInput]; + } + }); + } + + } +} + + +// session interrupted events +- (void)sessionWasInterrupted:(NSNotification *)notification +{ + // Mark session interruption + _sessionInterrupted = YES; + + // Turn on video interrupted if our session is interrupted + // for any reason + if ([self isRecording]) { + self.isRecordingInterrupted = YES; + } + + // prevent any video recording start that we might have on the way + _recordRequested = NO; + + // get event info and fire RN event if our session was interrupted + // due to audio being taken away. + NSDictionary *userInfo = notification.userInfo; + NSInteger type = [[userInfo valueForKey:AVCaptureSessionInterruptionReasonKey] integerValue]; + + if(type == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient){ + // if we have audio, stop it so preview resumes + // it will eventually be re-loaded the next time recording + // is requested, although it will flicker. + dispatch_async(self.sessionQueue, ^{ + [self removeAudioCaptureSessionInput]; + }); + + } + +} + + +// update flash and our interrupted flag on session resume +- (void)sessionDidStartRunning:(NSNotification *)notification +{ + //NSLog(@"sessionDidStartRunning Was interrupted? %d", _sessionInterrupted); + + if(_sessionInterrupted){ + // resume flash value since it will be resetted / turned off + dispatch_async(self.sessionQueue, ^{ + [self updateFlashMode]; + }); + } + + _sessionInterrupted = NO; +} + +- (void)sessionRuntimeError:(NSNotification *)notification +{ + // Manually restarting the session since it must + // have been stopped due to an error. + dispatch_async(self.sessionQueue, ^{ + _sessionInterrupted = NO; + [self.session startRunning]; + [self onReady:nil]; + }); +} + +- (void)orientationChanged:(NSNotification *)notification +{ + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + [self changePreviewOrientation:orientation]; +} + +- (void)changePreviewOrientation:(UIInterfaceOrientation)orientation +{ + __weak typeof(self) weakSelf = self; + AVCaptureVideoOrientation videoOrientation = [RNCameraUtils videoOrientationForInterfaceOrientation:orientation]; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(self) strongSelf = weakSelf; + if (strongSelf && strongSelf.previewLayer.connection.isVideoOrientationSupported) { + [strongSelf.previewLayer.connection setVideoOrientation:videoOrientation]; + } + }); +} +-(UIPinchGestureRecognizer*)createUIPinchGestureRecognizer +{ + return [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchToZoomRecognizer:)]; +} +- (void)setupOrDisablePinchZoom +{ + if([self useNativeZoom]){ + self.pinchGestureRecognizer=[self createUIPinchGestureRecognizer]; + [self addGestureRecognizer:self.pinchGestureRecognizer]; + }else{ + [self removeGestureRecognizer:self.pinchGestureRecognizer]; + self.pinchGestureRecognizer=nil; + } +} + +# pragma mark - AVCaptureMetadataOutput + +- (void)setupOrDisableBarcodeScanner +{ + [self _setupOrDisableMetadataOutput]; + [self _updateMetadataObjectsToRecognize]; +} + +- (void)updateRectOfInterest +{ + if (_metadataOutput == nil) { + return; + } + [_metadataOutput setRectOfInterest: _rectOfInterest]; +} + +- (void)_setupOrDisableMetadataOutput +{ + if ([self isReadingBarCodes] && (_metadataOutput == nil || ![self.session.outputs containsObject:_metadataOutput])) { + AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init]; + if ([self.session canAddOutput:metadataOutput]) { + [metadataOutput setMetadataObjectsDelegate:self queue:self.sessionQueue]; + [self.session addOutput:metadataOutput]; + self.metadataOutput = metadataOutput; + } + } else if (_metadataOutput != nil && ![self isReadingBarCodes]) { + [self.session removeOutput:_metadataOutput]; + _metadataOutput = nil; + } +} + +- (void)_updateMetadataObjectsToRecognize +{ + if (_metadataOutput == nil) { + return; + } + + NSArray *availableRequestedObjectTypes = [[NSArray alloc] init]; + NSArray *requestedObjectTypes = [NSArray arrayWithArray:self.barCodeTypes]; + NSArray *availableObjectTypes = _metadataOutput.availableMetadataObjectTypes; + + for(AVMetadataObjectType objectType in requestedObjectTypes) { + if ([availableObjectTypes containsObject:objectType]) { + availableRequestedObjectTypes = [availableRequestedObjectTypes arrayByAddingObject:objectType]; + } + } + + [_metadataOutput setMetadataObjectTypes:availableRequestedObjectTypes]; + [self updateRectOfInterest]; +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects + fromConnection:(AVCaptureConnection *)connection +{ + for(AVMetadataObject *metadata in metadataObjects) { + if([metadata isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) { + AVMetadataMachineReadableCodeObject *codeMetadata = (AVMetadataMachineReadableCodeObject *) metadata; + for (id barcodeType in self.barCodeTypes) { + if ([metadata.type isEqualToString:barcodeType]) { + AVMetadataMachineReadableCodeObject *transformed = (AVMetadataMachineReadableCodeObject *)[_previewLayer transformedMetadataObjectForMetadataObject:metadata]; + NSMutableDictionary *event = [NSMutableDictionary dictionaryWithDictionary:@{ + @"type" : codeMetadata.type, + @"data" : [NSNull null], + @"rawData" : [NSNull null], + @"bounds": @{ + @"origin": @{ + @"x": [NSString stringWithFormat:@"%f", transformed.bounds.origin.x], + @"y": [NSString stringWithFormat:@"%f", transformed.bounds.origin.y] + }, + @"size": @{ + @"height": [NSString stringWithFormat:@"%f", transformed.bounds.size.height], + @"width": [NSString stringWithFormat:@"%f", transformed.bounds.size.width] + } + } + } + ]; + + NSData *rawData; + // If we're on ios11 then we can use `descriptor` to access the raw data of the barcode. + // If we're on an older version of iOS we're stuck using valueForKeyPath to peak at the + // data. + if (@available(iOS 11, *)) { + // descriptor is a CIBarcodeDescriptor which is an abstract base class with no useful fields. + // in practice it's a subclass, many of which contain errorCorrectedPayload which is the data we + // want. Instead of individually checking the class types, just duck type errorCorrectedPayload + if ([codeMetadata.descriptor respondsToSelector:@selector(errorCorrectedPayload)]) { + rawData = [codeMetadata.descriptor performSelector:@selector(errorCorrectedPayload)]; + } + } else { + rawData = [codeMetadata valueForKeyPath:@"_internal.basicDescriptor.BarcodeRawData"]; + } + + // Now that we have the raw data of the barcode translate it into a hex string to pass to the JS + const unsigned char *dataBuffer = (const unsigned char *)[rawData bytes]; + if (dataBuffer) { + NSMutableString *rawDataHexString = [NSMutableString stringWithCapacity:([rawData length] * 2)]; + for (int i = 0; i < [rawData length]; ++i) { + [rawDataHexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; + } + [event setObject:[NSString stringWithString:rawDataHexString] forKey:@"rawData"]; + } + + // If we were able to extract a string representation of the barcode, attach it to the event as well + // else just send null along. + if (codeMetadata.stringValue) { + [event setObject:codeMetadata.stringValue forKey:@"data"]; + } + + // Only send the event if we were able to pull out a binary or string representation + if ([event objectForKey:@"data"] != [NSNull null] || [event objectForKey:@"rawData"] != [NSNull null]) { + [self onCodeRead:event]; + } + } + } + } + } +} + +# pragma mark - AVCaptureMovieFileOutput + +- (void)setupMovieFileCapture +{ + AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; + + if ([self.session canAddOutput:movieFileOutput]) { + [self.session addOutput:movieFileOutput]; + self.movieFileOutput = movieFileOutput; + } +} + +- (void)cleanupMovieFileCapture +{ + if ([_session.outputs containsObject:_movieFileOutput]) { + [_session removeOutput:_movieFileOutput]; + _movieFileOutput = nil; + } +} + +- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error +{ + BOOL success = YES; + if ([error code] != noErr) { + NSNumber *value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey]; + if (value) { + success = [value boolValue]; + } + } + if (success && self.videoRecordedResolve != nil) { + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + + void (^resolveBlock)(void) = ^() { + self.videoRecordedResolve(result); + }; + + result[@"uri"] = outputFileURL.absoluteString; + result[@"videoOrientation"] = @([self.orientation integerValue]); + result[@"deviceOrientation"] = @([self.deviceOrientation integerValue]); + result[@"isRecordingInterrupted"] = @(self.isRecordingInterrupted); + + + if (@available(iOS 10, *)) { + AVVideoCodecType videoCodec = self.videoCodecType; + if (videoCodec == nil) { + videoCodec = [self.movieFileOutput.availableVideoCodecTypes firstObject]; + } + result[@"codec"] = videoCodec; + + if ([connections[0] isVideoMirrored]) { + [self mirrorVideo:outputFileURL completion:^(NSURL *mirroredURL) { + result[@"uri"] = mirroredURL.absoluteString; + resolveBlock(); + }]; + return; + } + } + + resolveBlock(); + } else if (self.videoRecordedReject != nil) { + self.videoRecordedReject(@"E_RECORDING_FAILED", @"An error occurred while recording a video.", error); + } + + [self cleanupCamera]; + +} + +- (void)cleanupCamera { + self.videoRecordedResolve = nil; + self.videoRecordedReject = nil; + self.videoCodecType = nil; + self.deviceOrientation = nil; + self.orientation = nil; + self.isRecordingInterrupted = NO; + + if ([self.textDetector isRealDetector] || [self.faceDetector isRealDetector]) { + [self cleanupMovieFileCapture]; + } + + if ([self.textDetector isRealDetector]) { + [self setupOrDisableTextDetector]; + } + + if ([self.faceDetector isRealDetector]) { + [self setupOrDisableFaceDetector]; + } + + if ([self.barcodeDetector isRealDetector]) { + [self setupOrDisableBarcodeDetector]; + } + + // reset preset to current default + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)mirrorVideo:(NSURL *)inputURL completion:(void (^)(NSURL* outputUR))completion { + AVAsset* videoAsset = [AVAsset assetWithURL:inputURL]; + AVAssetTrack* clipVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject]; + + AVMutableComposition* composition = [[AVMutableComposition alloc] init]; + [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; + + AVMutableVideoComposition* videoComposition = [[AVMutableVideoComposition alloc] init]; + videoComposition.renderSize = CGSizeMake(clipVideoTrack.naturalSize.height, clipVideoTrack.naturalSize.width); + videoComposition.frameDuration = CMTimeMake(1, 30); + + AVMutableVideoCompositionLayerInstruction* transformer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:clipVideoTrack]; + + AVMutableVideoCompositionInstruction* instruction = [[AVMutableVideoCompositionInstruction alloc] init]; + instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30)); + + CGAffineTransform transform = CGAffineTransformMakeScale(-1.0, 1.0); + transform = CGAffineTransformTranslate(transform, -clipVideoTrack.naturalSize.width, 0); + transform = CGAffineTransformRotate(transform, M_PI/2.0); + transform = CGAffineTransformTranslate(transform, 0.0, -clipVideoTrack.naturalSize.width); + + [transformer setTransform:transform atTime:kCMTimeZero]; + + [instruction setLayerInstructions:@[transformer]]; + [videoComposition setInstructions:@[instruction]]; + + //get preset for export via default or session + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + preset = self.session.sessionPreset; + } + + // Export + AVAssetExportSession* exportSession = [AVAssetExportSession exportSessionWithAsset:videoAsset presetName:preset]; + NSString* filePath = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingString:@"CameraFlip"] withExtension:@".mp4"]; + NSURL* outputURL = [NSURL fileURLWithPath:filePath]; + [exportSession setOutputURL:outputURL]; + [exportSession setOutputFileType:AVFileTypeMPEG4]; + [exportSession setVideoComposition:videoComposition]; + [exportSession exportAsynchronouslyWithCompletionHandler:^{ + if (exportSession.status == AVAssetExportSessionStatusCompleted) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(outputURL); + }); + } else { + NSLog(@"Export failed %@", exportSession.error); + } + }]; +} + +# pragma mark - FaceDetectorMlkit + +-(id)createFaceDetectorMlKit +{ + Class faceDetectorManagerClassMlkit = NSClassFromString(@"FaceDetectorManagerMlkit"); + return [[faceDetectorManagerClassMlkit alloc] init]; +} + +- (void)setupOrDisableFaceDetector +{ + if (self.canDetectFaces && [self.faceDetector isRealDetector]){ + AVCaptureSessionPreset preset = [self getDefaultPresetVideo]; + + self.session.sessionPreset = preset; + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopFaceDetection]; + return; + } + + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopFaceDetection]; + } +} + +- (void)stopFaceDetection +{ + if (self.videoDataOutput && !self.canReadText) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)updateTrackingEnabled:(id)requestedTracking +{ + [self.faceDetector setTracking:requestedTracking queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionMode:(id)requestedMode +{ + [self.faceDetector setPerformanceMode:requestedMode queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionLandmarks:(id)requestedLandmarks +{ + [self.faceDetector setLandmarksMode:requestedLandmarks queue:self.sessionQueue]; +} + +- (void)updateFaceDetectionClassifications:(id)requestedClassifications +{ + [self.faceDetector setClassificationMode:requestedClassifications queue:self.sessionQueue]; +} + +- (void)onFacesDetected:(NSDictionary *)event +{ + if (_onFacesDetected && _session) { + _onFacesDetected(event); + } +} + +# pragma mark - BarcodeDetectorMlkit + +-(id)createBarcodeDetectorMlKit +{ + Class barcodeDetectorManagerClassMlkit = NSClassFromString(@"BarcodeDetectorManagerMlkit"); + return [[barcodeDetectorManagerClassMlkit alloc] init]; +} + +- (void)setupOrDisableBarcodeDetector +{ + if (self.canDetectBarcodes && [self.barcodeDetector isRealDetector]){ + AVCaptureSessionPreset preset = [self getDefaultPresetVideo]; + + self.session.sessionPreset = preset; + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopBarcodeDetection]; + return; + } + + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopBarcodeDetection]; + } +} + +- (void)stopBarcodeDetection +{ + if (self.videoDataOutput && !self.canReadText) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; + AVCaptureSessionPreset preset = [self getDefaultPreset]; + if (self.session.sessionPreset != preset) { + [self updateSessionPreset: preset]; + } +} + +- (void)updateGoogleVisionBarcodeType:(id)requestedTypes +{ + [self.barcodeDetector setType:requestedTypes queue:self.sessionQueue]; +} + +- (void)updateGoogleVisionBarcodeMode:(id)requestedMode +{ + if ([self.barcodeDetector isRealDetector]) { + [self.barcodeDetector setMode:requestedMode queue:self.sessionQueue]; + } +} + +- (void)onBarcodesDetected:(NSDictionary *)event +{ + if (_onGoogleVisionBarcodesDetected && _session) { + _onGoogleVisionBarcodesDetected(event); + } +} + +# pragma mark - TextDetector + +-(id)createTextDetector +{ + Class textDetectorManagerClass = NSClassFromString(@"TextDetectorManager"); + return [[textDetectorManagerClass alloc] init]; +} + +- (void)setupOrDisableTextDetector +{ + if ([self canReadText] && [self.textDetector isRealDetector]){ + if (!self.videoDataOutput) { + self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + if (![self.session canAddOutput:_videoDataOutput]) { + NSLog(@"Failed to setup video data output"); + [self stopTextRecognition]; + return; + } + NSDictionary *rgbOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCMPixelFormat_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + [self.videoDataOutput setVideoSettings:rgbOutputSettings]; + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + [self.videoDataOutput setSampleBufferDelegate:self queue:self.sessionQueue]; + [self.session addOutput:_videoDataOutput]; + } + } else { + [self stopTextRecognition]; + } +} + +- (void)stopTextRecognition +{ + if (self.videoDataOutput && !self.canDetectFaces) { + [self.session removeOutput:self.videoDataOutput]; + } + self.videoDataOutput = nil; +} + +# pragma mark - mlkit + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection +{ + if (![self.textDetector isRealDetector] && ![self.faceDetector isRealDetector] && ![self.barcodeDetector isRealDetector]) { + NSLog(@"failing real check"); + return; + } + + // Do not submit image for text/face recognition too often: + // 1. we only dispatch events every 500ms anyway + // 2. wait until previous recognition is finished + // 3. let user disable text recognition, e.g. onTextRecognized={someCondition ? null : this.textRecognized} + NSDate *methodFinish = [NSDate date]; + NSTimeInterval timePassedSinceSubmittingForText = [methodFinish timeIntervalSinceDate:self.startText]; + NSTimeInterval timePassedSinceSubmittingForFace = [methodFinish timeIntervalSinceDate:self.startFace]; + BOOL canSubmitForTextDetection = timePassedSinceSubmittingForText > 0.5 && _finishedReadingText && self.canReadText && [self.textDetector isRealDetector]; + BOOL canSubmitForFaceDetection = timePassedSinceSubmittingForFace > 0.5 && _finishedDetectingFace && self.canDetectFaces && [self.faceDetector isRealDetector]; + BOOL canSubmitForBarcodeDetection = self.canDetectBarcodes && [self.barcodeDetector isRealDetector]; + if (canSubmitForFaceDetection || canSubmitForTextDetection || canSubmitForBarcodeDetection) { + CGSize previewSize = CGSizeMake(_previewLayer.frame.size.width, _previewLayer.frame.size.height); + NSInteger position = self.videoCaptureDeviceInput.device.position; + UIImage *image = [RNCameraUtils convertBufferToUIImage:sampleBuffer previewSize:previewSize position:position]; + // take care of the fact that preview dimensions differ from the ones of the image that we submit for text detection + float scaleX = _previewLayer.frame.size.width / image.size.width; + float scaleY = _previewLayer.frame.size.height / image.size.height; + + // find text features + if (canSubmitForTextDetection) { + _finishedReadingText = false; + self.startText = [NSDate date]; + [self.textDetector findTextBlocksInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * textBlocks) { + NSDictionary *eventText = @{@"type" : @"TextBlock", @"textBlocks" : textBlocks}; + [self onText:eventText]; + self.finishedReadingText = true; + }]; + } + // find face features + if (canSubmitForFaceDetection) { + _finishedDetectingFace = false; + self.startFace = [NSDate date]; + [self.faceDetector findFacesInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * faces) { + NSDictionary *eventFace = @{@"type" : @"face", @"faces" : faces}; + [self onFacesDetected:eventFace]; + self.finishedDetectingFace = true; + }]; + } + // find barcodes + if (canSubmitForBarcodeDetection) { + // Check for the barcode detection mode (Normal, Alternate, Inverted) + switch ([self.barcodeDetector fetchDetectionMode]) { + case RNCameraGoogleVisionBarcodeModeNormal: + self.invertImageData = false; + break; + case RNCameraGoogleVisionBarcodeModeAlternate: + self.invertImageData = !self.invertImageData; + break; + case RNCameraGoogleVisionBarcodeModeInverted: + self.invertImageData = true; + break; + default: + self.invertImageData = false; + break; + } + + if (self.invertImageData) { + image = [RNImageUtils invertColors:image]; + } + + [self.barcodeDetector findBarcodesInFrame:image scaleX:scaleX scaleY:scaleY completed:^(NSArray * barcodes) { + NSDictionary *eventBarcode = @{@"type" : @"barcode", @"barcodes" : barcodes}; + [self onBarcodesDetected:eventBarcode]; + }]; + } + } +} + +- (bool)isRecording { + return self.movieFileOutput != nil ? self.movieFileOutput.isRecording : NO; +} + +@end diff --git a/packages/rnv/pluginTemplates/react-native-dynamic-fonts/overrides/android/src/main/java/org/th317erd/react/DynamicFontsModule.java b/packages/rnv/pluginTemplates/react-native-dynamic-fonts/overrides/android/src/main/java/org/th317erd/react/DynamicFontsModule.java new file mode 100644 index 0000000000..0caf5fac7b --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-dynamic-fonts/overrides/android/src/main/java/org/th317erd/react/DynamicFontsModule.java @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2017-present, Wyatt Greenway. All rights reserved. + * + * This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ + +package org.th317erd.react; + +import android.app.Activity; +import android.graphics.Typeface; +import android.util.Base64; +import android.util.Log; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.views.text.ReactFontManager; + +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +class DynamicFontsModule extends ReactContextBaseJavaModule { + int tempNameCounter = 0; + WritableMap response; + + public DynamicFontsModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public String getName() { + return "DynamicFonts"; + } + + @ReactMethod + public void loadFontFromFile(final ReadableMap options, final Callback callback) { + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + callback.invoke("Invalid activity"); + return; + } + + String filePath = options.hasKey("filePath") ? options.getString("filePath") : null, + name = (options.hasKey("name")) ? options.getString("name") : null; + + if (filePath == null || filePath.length() == 0) { + callback.invoke("filePath property empty"); + return; + } + + File f = new File(filePath); + + if (f.exists() && f.canRead()) { + boolean wasLoaded = false; + try { + Typeface typeface = Typeface.createFromFile(f); + //Cache the font for react + ReactFontManager.getInstance().setTypeface(name, typeface.getStyle(), typeface); + wasLoaded = true; + } catch (Throwable e) { + callback.invoke(e.getMessage()); + } finally { + if (wasLoaded) { + callback.invoke(null, name); + } + } + } else { + callback.invoke("invalid file"); + } + } + + @ReactMethod + public void loadFont(final ReadableMap options, final Callback callback) throws Exception { + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + callback.invoke("Invalid activity"); + return; + } + + String name = (options.hasKey("name")) ? options.getString("name") : null, + data = (options.hasKey("data")) ? options.getString("data") : null, + type = null; + + if (name == null || name.length() == 0) { + callback.invoke("Name property empty"); + return; + } + + if (data == null || data.length() == 0) { + callback.invoke("Data property empty"); + return; + } + + if (("data:").equalsIgnoreCase(data.substring(0, 5))) { + Integer pos = data.indexOf(','); + if (pos > 0) { + String[] encodingParams = data.substring(5, pos).split(";"); + String mimeType = encodingParams[0]; + + data = data.substring(pos + 1); + + if (("application/x-font-ttf").equalsIgnoreCase(mimeType) || + ("application/x-font-truetype").equalsIgnoreCase(mimeType) || + ("font/ttf").equalsIgnoreCase(mimeType)) { + type = "ttf"; + } else if (("application/x-font-opentype").equalsIgnoreCase(mimeType) || + ("font/opentype").equalsIgnoreCase(mimeType)) { + type = "otf"; + } + } + } + + if (options.hasKey("type")) + type = options.getString("type"); + + if (type == null) + type = "ttf"; + + try { + byte[] decodedBytes = Base64.decode(data, Base64.DEFAULT); + File cacheFile = new File(currentActivity.getFilesDir(), "tempFont" + (tempNameCounter++) + type); + + FileOutputStream stream = new FileOutputStream(cacheFile); + try { + stream.write(decodedBytes); + } finally { + stream.close(); + } + + //Load the font from the temporary file we just created + Typeface typeface = Typeface.createFromFile(cacheFile); + + if (typeface.isBold()) + name = name + "_bold"; + + if (typeface.isItalic()) + name = name + "_italic"; + + //Cache the font for react + ReactFontManager.getInstance().setTypeface(name, typeface.getStyle(), typeface); + + cacheFile.delete(); + + callback.invoke(null, name); + } catch(Exception e) { + callback.invoke(e.getMessage()); + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-home-indicator/overrides/ios/RNHomeIndicator.h b/packages/rnv/pluginTemplates/react-native-home-indicator/overrides/ios/RNHomeIndicator.h new file mode 100644 index 0000000000..fc26ffb490 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-home-indicator/overrides/ios/RNHomeIndicator.h @@ -0,0 +1,10 @@ +#import +#import + + +@interface HomeIndicatorViewController : UIViewController +@property BOOL prefersAutoHidden; +@end + +@interface RNHomeIndicator : NSObject +@end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-image-picker/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-image-picker/overrides/android/build.gradle new file mode 100644 index 0000000000..2d2aa9989c --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-image-picker/overrides/android/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.library' + +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 29) + + defaultConfig { + minSdkVersion 21 + targetSdkVersion safeExtGet('targetSdkVersion', 29) + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation "com.facebook.react:react-native:+" + implementation "androidx.core:core:1.3.1" + implementation "androidx.exifinterface:exifinterface:1.3.1" +} diff --git a/packages/rnv/pluginTemplates/react-native-maps/overrides/react-native-maps.podspec b/packages/rnv/pluginTemplates/react-native-maps/overrides/react-native-maps.podspec index 0ed97b71f7..04e1f4d1fc 100644 --- a/packages/rnv/pluginTemplates/react-native-maps/overrides/react-native-maps.podspec +++ b/packages/rnv/pluginTemplates/react-native-maps/overrides/react-native-maps.podspec @@ -8,12 +8,12 @@ Pod::Spec.new do |s| s.summary = "React Native Mapview component for iOS + Android" s.authors = { "intelligibabble" => "leland.m.richardson@gmail.com" } - s.homepage = "https://github.com/airbnb/react-native-maps#readme" + s.homepage = "https://github.com/react-native-maps/react-native-maps#readme" s.license = "MIT" - s.platforms = { :ios => "8.0", :tvos => "9.2" } + s.platform = :ios, "11.0" - s.source = { :git => "https://github.com/airbnb/react-native-maps.git" } - s.source_files = "lib/ios/AirMaps/**/*.{h,m}" + s.source = { :git => "https://github.com/react-native-maps/react-native-maps.git", :tag=> "v#{s.version}" } + s.source_files = "ios/AirMaps/**/*.{h,m}" - s.dependency 'React' -end + s.dependency 'React-Core' +end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-pdf-view/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-pdf-view/overrides/android/build.gradle new file mode 100644 index 0000000000..cd3b7c4d7b --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-pdf-view/overrides/android/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.library' + +description = 'android-pdfview' + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.3.1' + } +} + +android { + compileSdkVersion 23 + buildToolsVersion '23.0.1' + + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 23 + } +} + +repositories { + jcenter() +} + +dependencies { + compile ('com.facebook.react:react-native:+') { + exclude group: 'com.facebook.fresco', module: 'imagepipeline-okhttp' + exclude group: 'com.squareup.okhttp', module: 'okhttp' + exclude group: 'com.squareup.okhttp', module: 'okhttp-ws' + } + compile 'com.joanzapata.pdfview:android-pdfview:1.0.4@aar' +} diff --git a/packages/rnv/pluginTemplates/react-native-pdf/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-pdf/overrides/android/build.gradle new file mode 100644 index 0000000000..c1fccfe6d6 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-pdf/overrides/android/build.gradle @@ -0,0 +1,66 @@ +description = 'react-native-pdf' + +buildscript { + repositories { + mavenCentral() + google() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.1.4' + } +} + +repositories { + mavenCentral() + maven { + url 'https://jitpack.io' + content { + // Use Jitpack only for AndroidPdfViewer; the rest is hosted at mavenCentral. + includeGroup "com.github.TalbotGooday" + } + } +} + +apply plugin: 'com.android.library' + +def _ext = rootProject.ext + +def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+' +def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion : 28 +def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '28.0.3' +def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 16 +def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 28 + + + +android { + compileSdkVersion _compileSdkVersion + buildToolsVersion _buildToolsVersion + + defaultConfig { + minSdkVersion _minSdkVersion + targetSdkVersion _targetSdkVersion + } + + lintOptions { + abortOnError true + } + + packagingOptions { + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libjsc.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libjsc.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + } +} + +dependencies { + implementation "com.facebook.react:react-native:${_reactNativeVersion}" + // NOTE: The original repo at com.github.barteksc is abandoned by the maintainer; there will be no more updates coming from that repo. + // It was taken over by com.github.TalbotGooday; from now on please use this repo until (if ever) the Barteksc repo is resumed. + implementation 'com.github.TalbotGooday:AndroidPdfViewer:3.1.0-beta.3' + implementation 'com.google.code.gson:gson:2.8.9' +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow1.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow1.png new file mode 100644 index 0000000000000000000000000000000000000000..de46111c6f86d52f34ec73bc51eabe817e0db07b GIT binary patch literal 8410 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 Hr@#MSO;e>K literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow4.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow4.png new file mode 100644 index 0000000000000000000000000000000000000000..2f029f512140afeb90fd9e95629c3b975466bffe GIT binary patch literal 5769 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 G+5TUGL6E`# literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow5.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/android/app/src/main/res/drawable/arrow5.png new file mode 100644 index 0000000000000000000000000000000000000000..b83a5d7e35ae13313311f0dbc4ea8de0b00d4250 GIT binary patch literal 5768 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 A0RR91 literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.h b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.h new file mode 100644 index 0000000000..855f3ed793 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.h @@ -0,0 +1,8 @@ +#import +#import +#import + +@interface RNPhotoEditor : NSObject + +@end + diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.m b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.m new file mode 100644 index 0000000000..84b47a435f --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/RNVApp/RNPhotoEditor/RNPhotoEditor.m @@ -0,0 +1,147 @@ + +#import "RNPhotoEditor.h" + +@implementation RNPhotoEditor + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} +RCT_EXPORT_MODULE() + +NSString *_editImagePath = nil; + +RCTResponseSenderBlock _onDoneEditing = nil; +RCTResponseSenderBlock _onCancelEditing = nil; + +- (void)doneEditingWithImage:(UIImage *)image { + if (_onDoneEditing == nil) return; + + // Save image. + NSString *newImagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject]; + NSString *fileName = [[_editImagePath componentsSeparatedByString:@"/"].lastObject componentsSeparatedByString:@"."].firstObject; + fileName = [fileName stringByAppendingString:@".png"]; + newImagePath = [newImagePath stringByAppendingPathComponent:fileName]; + [UIImagePNGRepresentation(image) writeToFile:newImagePath atomically:YES]; + + _onDoneEditing(@[newImagePath]); +} + +- (void)canceledEditing { + if (_onCancelEditing == nil) return; + + _onCancelEditing(@[]); +} + +RCT_EXPORT_METHOD(Edit:(nonnull NSDictionary *)props onDone:(RCTResponseSenderBlock)onDone onCancel:(RCTResponseSenderBlock)onCancel) { + + dispatch_async(dispatch_get_main_queue(), ^{ + _editImagePath = [props objectForKey: @"path"]; + + _onDoneEditing = onDone; + _onCancelEditing = onCancel; + + PhotoEditorViewController *photoEditor = [[PhotoEditorViewController alloc] initWithNibName:@"PhotoEditorViewController" bundle: [NSBundle bundleForClass:[PhotoEditorViewController class]]]; + + // Process Image for Editing + UIImage *image = [UIImage imageWithContentsOfFile:_editImagePath]; + if (image == nil) { + NSURL *url = [NSURL URLWithString:_editImagePath]; + NSData *data = [NSData dataWithContentsOfURL:url]; + + image = [UIImage imageWithData:data]; + } + + photoEditor.image = image; + + // Process Stickers + NSArray *stickers = [props objectForKey: @"stickers"]; + NSMutableArray *imageStickers = [[NSMutableArray alloc] initWithCapacity:stickers.count]; + + for (NSString *sticker in stickers) { + NSString *stickerFile = [sticker stringByAppendingString:@".png"]; + UIImage *image = [UIImage imageNamed: stickerFile]; + if(image){ + [imageStickers addObject: image]; + } + } + + photoEditor.stickers = imageStickers; + + //Process Controls + NSArray *hiddenControls = [props objectForKey: @"hiddenControls"]; + NSMutableArray *passHiddenControls = [[NSMutableArray alloc] initWithCapacity:hiddenControls.count]; + + for (NSString *hiddenControl in hiddenControls) { + [passHiddenControls addObject: [[NSString alloc] initWithString: hiddenControl]]; + } + + photoEditor.hiddenControls = passHiddenControls; + + //Process Colors + NSArray *colors = [props objectForKey: @"colors"]; + NSMutableArray *passColors = [[NSMutableArray alloc] initWithCapacity:colors.count]; + + for (NSString *color in colors) { + [passColors addObject: [self colorWithHexString: color]]; + } + + photoEditor.colors = passColors; + + if (@available(iOS 13, *)) { + [photoEditor setModalPresentationStyle: UIModalPresentationFullScreen]; + } + + // Invoke Editor + photoEditor.photoEditorDelegate = self; + + id app = [[UIApplication sharedApplication] delegate]; + [((UINavigationController*) app.window.rootViewController) presentViewController:photoEditor animated:YES completion:nil]; + }); +} + + +- (CGFloat) colorComponentFrom: (NSString *) string start: (NSUInteger) start length: (NSUInteger) length { + NSString *substring = [string substringWithRange: NSMakeRange(start, length)]; + NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring]; + unsigned hexComponent; + [[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent]; + return hexComponent / 255.0; +} + +- (UIColor *) colorWithHexString: (NSString *) hexString { + NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString]; + CGFloat alpha, red, blue, green; + switch ([colorString length]) { + case 3: // #RGB + alpha = 1.0f; + red = [self colorComponentFrom: colorString start: 0 length: 1]; + green = [self colorComponentFrom: colorString start: 1 length: 1]; + blue = [self colorComponentFrom: colorString start: 2 length: 1]; + break; + case 4: // #ARGB + alpha = [self colorComponentFrom: colorString start: 0 length: 1]; + red = [self colorComponentFrom: colorString start: 1 length: 1]; + green = [self colorComponentFrom: colorString start: 2 length: 1]; + blue = [self colorComponentFrom: colorString start: 3 length: 1]; + break; + case 6: // #RRGGBB + alpha = 1.0f; + red = [self colorComponentFrom: colorString start: 0 length: 2]; + green = [self colorComponentFrom: colorString start: 2 length: 2]; + blue = [self colorComponentFrom: colorString start: 4 length: 2]; + break; + case 8: // #AARRGGBB + alpha = [self colorComponentFrom: colorString start: 0 length: 2]; + red = [self colorComponentFrom: colorString start: 2 length: 2]; + green = [self colorComponentFrom: colorString start: 4 length: 2]; + blue = [self colorComponentFrom: colorString start: 6 length: 2]; + break; + default: + return nil; + } + return [UIColor colorWithRed: red green: green blue: blue alpha: alpha]; +} + + +@end diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow1.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow1.png new file mode 100644 index 0000000000000000000000000000000000000000..de46111c6f86d52f34ec73bc51eabe817e0db07b GIT binary patch literal 8410 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 Hr@#MSO;e>K literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow4.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow4.png new file mode 100644 index 0000000000000000000000000000000000000000..2f029f512140afeb90fd9e95629c3b975466bffe GIT binary patch literal 5769 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 G+5TUGL6E`# literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow5.png b/packages/rnv/pluginTemplates/react-native-photo-editor/builds/ios/Resources/arrow5.png new file mode 100644 index 0000000000000000000000000000000000000000..b83a5d7e35ae13313311f0dbc4ea8de0b00d4250 GIT binary patch literal 5768 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 A0RR91 literal 0 HcmV?d00001 diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/build.gradle new file mode 100644 index 0000000000..39e601e048 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/build.gradle @@ -0,0 +1,46 @@ +// NOTE: adding exif interface +buildscript { + repositories { + maven { url 'https://jitpack.io' } + maven { url "https://maven.google.com" } + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.1.3' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.2' + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenCentral() + maven { url 'https://jitpack.io' } + maven { url "https://maven.google.com" } +} + +dependencies { + implementation 'com.facebook.react:react-native:+' + implementation 'com.github.prscX:photo-editor-android:master' + implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1@aar' + implementation 'com.nineoldandroids:library:2.4.0' + implementation 'com.android.support:design:27.1.1' + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:exifinterface:28.0.0' +} diff --git a/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/src/main/java/com/ahmedadeltito/photoeditor/PhotoEditorActivity.java b/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/src/main/java/com/ahmedadeltito/photoeditor/PhotoEditorActivity.java new file mode 100644 index 0000000000..48b0abdbd2 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-photo-editor/overrides/android/src/main/java/com/ahmedadeltito/photoeditor/PhotoEditorActivity.java @@ -0,0 +1,695 @@ +// NOTE: adding exif interface +package com.ahmedadeltito.photoeditor; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Typeface; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.provider.MediaStore; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.net.Uri; +import android.view.WindowManager; + +import com.ahmedadeltito.photoeditor.widget.SlidingUpPanelLayout; +import com.ahmedadeltito.photoeditorsdk.BrushDrawingView; +import com.ahmedadeltito.photoeditorsdk.OnPhotoEditorSDKListener; +import com.ahmedadeltito.photoeditorsdk.PhotoEditorSDK; +import com.ahmedadeltito.photoeditorsdk.ViewType; +import com.viewpagerindicator.PageIndicator; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import ui.photoeditor.R; + +import android.graphics.Matrix; +import androidx.exifinterface.media.ExifInterface; + +public class PhotoEditorActivity extends AppCompatActivity implements View.OnClickListener, OnPhotoEditorSDKListener { + + public static Typeface emojiFont = null; + + private final String TAG = "PhotoEditorActivity"; + private RelativeLayout parentImageRelativeLayout; + private RecyclerView drawingViewColorPickerRecyclerView; + private TextView undoTextView, undoTextTextView, doneDrawingTextView, eraseDrawingTextView; + private SlidingUpPanelLayout mLayout; + private View topShadow; + private RelativeLayout topShadowRelativeLayout; + private View bottomShadow; + private RelativeLayout bottomShadowRelativeLayout; + private ArrayList colorPickerColors; + private int colorCodeTextView = -1; + private PhotoEditorSDK photoEditorSDK; + + private int imageOrientation; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_photo_editor); + + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + + String selectedImagePath = getIntent().getExtras().getString("selectedImagePath"); + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = 1; + + + Typeface newFont = getFontFromRes(R.raw.eventtusicons); + emojiFont = getFontFromRes(R.raw.emojioneandroid); + + BrushDrawingView brushDrawingView = (BrushDrawingView) findViewById(R.id.drawing_view); + drawingViewColorPickerRecyclerView = (RecyclerView) findViewById(R.id.drawing_view_color_picker_recycler_view); + parentImageRelativeLayout = (RelativeLayout) findViewById(R.id.parent_image_rl); + TextView closeTextView = (TextView) findViewById(R.id.close_tv); + TextView addTextView = (TextView) findViewById(R.id.add_text_tv); + TextView addPencil = (TextView) findViewById(R.id.add_pencil_tv); + RelativeLayout deleteRelativeLayout = (RelativeLayout) findViewById(R.id.delete_rl); + TextView deleteTextView = (TextView) findViewById(R.id.delete_tv); + TextView addImageEmojiTextView = (TextView) findViewById(R.id.add_image_emoji_tv); + TextView saveTextView = (TextView) findViewById(R.id.save_tv); + TextView saveTextTextView = (TextView) findViewById(R.id.save_text_tv); + undoTextView = (TextView) findViewById(R.id.undo_tv); + undoTextTextView = (TextView) findViewById(R.id.undo_text_tv); + doneDrawingTextView = (TextView) findViewById(R.id.done_drawing_tv); + eraseDrawingTextView = (TextView) findViewById(R.id.erase_drawing_tv); + TextView clearAllTextView = (TextView) findViewById(R.id.clear_all_tv); + TextView clearAllTextTextView = (TextView) findViewById(R.id.clear_all_text_tv); + TextView goToNextTextView = (TextView) findViewById(R.id.go_to_next_screen_tv); + ImageView photoEditImageView = (ImageView) findViewById(R.id.photo_edit_iv); + mLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout); + topShadow = findViewById(R.id.top_shadow); + topShadowRelativeLayout = (RelativeLayout) findViewById(R.id.top_parent_rl); + bottomShadow = findViewById(R.id.bottom_shadow); + bottomShadowRelativeLayout = (RelativeLayout) findViewById(R.id.bottom_parent_rl); + + ViewPager pager = (ViewPager) findViewById(R.id.image_emoji_view_pager); + PageIndicator indicator = (PageIndicator) findViewById(R.id.image_emoji_indicator); + + Bitmap rotatedBitmap; + Uri path; + String parsedPath; + String uriString; + Bitmap bitmap = null; + try { + path = Uri.parse(selectedImagePath); + parsedPath = UtilFunctions.getPath(this, path); + uriString = null != parsedPath ? parsedPath : path.toString(); + bitmap = BitmapFactory.decodeFile(uriString, options); + + ExifInterface exif = new ExifInterface(parsedPath); + imageOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); + rotatedBitmap = rotateBitmap(bitmap, imageOrientation, false); + + photoEditImageView.setImageBitmap(rotatedBitmap); + } catch (Exception e) { + Log.e("Error parsing Bitmap", e.getMessage()); + + if (bitmap != null) { + photoEditImageView.setImageBitmap(bitmap); + } + } + + closeTextView.setTypeface(newFont); + addTextView.setTypeface(newFont); + addPencil.setTypeface(newFont); + addImageEmojiTextView.setTypeface(newFont); + saveTextView.setTypeface(newFont); + undoTextView.setTypeface(newFont); + clearAllTextView.setTypeface(newFont); + goToNextTextView.setTypeface(newFont); + deleteTextView.setTypeface(newFont); + + final List fragmentsList = new ArrayList<>(); + + ImageFragment imageFragment = new ImageFragment(); + ArrayList stickers = (ArrayList) getIntent().getExtras().getSerializable("stickers"); + if (stickers != null && stickers.size() > 0) { + Bundle bundle = new Bundle(); + bundle.putSerializable("stickers", stickers); + + imageFragment.setArguments(bundle); + } + + fragmentsList.add(imageFragment); + + EmojiFragment emojiFragment = new EmojiFragment(); + fragmentsList.add(emojiFragment); + + PreviewSlidePagerAdapter adapter = new PreviewSlidePagerAdapter(getSupportFragmentManager(), fragmentsList); + pager.setAdapter(adapter); + pager.setOffscreenPageLimit(5); + indicator.setViewPager(pager); + + photoEditorSDK = new PhotoEditorSDK.PhotoEditorSDKBuilder(PhotoEditorActivity.this) + .parentView(parentImageRelativeLayout) // add parent image view + .childView(photoEditImageView) // add the desired image view + .deleteView(deleteRelativeLayout) // add the deleted view that will appear during the movement of the views + .brushDrawingView(brushDrawingView) // add the brush drawing view that is responsible for drawing on the image view + .buildPhotoEditorSDK(); // build photo editor sdk + photoEditorSDK.setOnPhotoEditorSDKListener(this); + + pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + if (position == 0) + mLayout.setScrollableView(((ImageFragment) fragmentsList.get(position)).imageRecyclerView); + else if (position == 1) + mLayout.setScrollableView(((EmojiFragment) fragmentsList.get(position)).emojiRecyclerView); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + closeTextView.setOnClickListener(this); + addImageEmojiTextView.setOnClickListener(this); + addTextView.setOnClickListener(this); + addPencil.setOnClickListener(this); + saveTextView.setOnClickListener(this); + saveTextTextView.setOnClickListener(this); + undoTextView.setOnClickListener(this); + undoTextTextView.setOnClickListener(this); + doneDrawingTextView.setOnClickListener(this); + eraseDrawingTextView.setOnClickListener(this); + clearAllTextView.setOnClickListener(this); + clearAllTextTextView.setOnClickListener(this); + goToNextTextView.setOnClickListener(this); + + ArrayList intentColors = (ArrayList) getIntent().getExtras().getSerializable("colorPickerColors"); + + colorPickerColors = new ArrayList<>(); + if (intentColors != null) { + colorPickerColors = intentColors; + } else { + colorPickerColors.add(getResources().getColor(R.color.black)); + colorPickerColors.add(getResources().getColor(R.color.blue_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.brown_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.green_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.orange_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.red_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.red_orange_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.sky_blue_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.violet_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.white)); + colorPickerColors.add(getResources().getColor(R.color.yellow_color_picker)); + colorPickerColors.add(getResources().getColor(R.color.yellow_green_color_picker)); + } + + + new CountDownTimer(500, 100) { + + public void onTick(long millisUntilFinished) { + } + + public void onFinish() { + mLayout.setScrollableView(((ImageFragment) fragmentsList.get(0)).imageRecyclerView); + } + + }.start(); + + ArrayList hiddenControls = (ArrayList) getIntent().getExtras().getSerializable("hiddenControls"); + for (int i = 0;i < hiddenControls.size();i++) { + if (hiddenControls.get(i).toString().equalsIgnoreCase("text")) { + addTextView.setVisibility(View.INVISIBLE); + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("clear")) { + clearAllTextView.setVisibility(View.INVISIBLE); + clearAllTextTextView.setVisibility(View.INVISIBLE); + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("crop")) { + + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("draw")) { + addPencil.setVisibility(View.INVISIBLE); + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("save")) { + saveTextTextView.setVisibility(View.INVISIBLE); + saveTextView.setVisibility(View.INVISIBLE); + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("share")) { + + } + if (hiddenControls.get(i).toString().equalsIgnoreCase("sticker")) { + addImageEmojiTextView.setVisibility(View.INVISIBLE); + } + } + } + + private boolean stringIsNotEmpty(String string) { + if (string != null && !string.equals("null")) { + if (!string.trim().equals("")) { + return true; + } + } + return false; + } + + public void addEmoji(String emojiName) { + photoEditorSDK.addEmoji(emojiName, emojiFont); + if (mLayout != null) + mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + } + + public void addImage(Bitmap image) { + photoEditorSDK.addImage(image); + if (mLayout != null) + mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + } + + private void addText(String text, int colorCodeTextView) { + photoEditorSDK.addText(text, colorCodeTextView); + } + + private void clearAllViews() { + photoEditorSDK.clearAllViews(); + } + + private void undoViews() { + photoEditorSDK.viewUndo(); + } + + private void eraseDrawing() { + photoEditorSDK.brushEraser(); + } + + private void openAddTextPopupWindow(String text, int colorCode) { + colorCodeTextView = colorCode; + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View addTextPopupWindowRootView = inflater.inflate(R.layout.add_text_popup_window, null); + final EditText addTextEditText = (EditText) addTextPopupWindowRootView.findViewById(R.id.add_text_edit_text); + TextView addTextDoneTextView = (TextView) addTextPopupWindowRootView.findViewById(R.id.add_text_done_tv); + RecyclerView addTextColorPickerRecyclerView = (RecyclerView) addTextPopupWindowRootView.findViewById(R.id.add_text_color_picker_recycler_view); + LinearLayoutManager layoutManager = new LinearLayoutManager(PhotoEditorActivity.this, LinearLayoutManager.HORIZONTAL, false); + addTextColorPickerRecyclerView.setLayoutManager(layoutManager); + addTextColorPickerRecyclerView.setHasFixedSize(true); + ColorPickerAdapter colorPickerAdapter = new ColorPickerAdapter(PhotoEditorActivity.this, colorPickerColors); + colorPickerAdapter.setOnColorPickerClickListener(new ColorPickerAdapter.OnColorPickerClickListener() { + @Override + public void onColorPickerClickListener(int colorCode) { + addTextEditText.setTextColor(colorCode); + colorCodeTextView = colorCode; + } + }); + addTextColorPickerRecyclerView.setAdapter(colorPickerAdapter); + if (stringIsNotEmpty(text)) { + addTextEditText.setText(text); + addTextEditText.setTextColor(colorCode == -1 ? getResources().getColor(R.color.white) : colorCode); + } + final PopupWindow pop = new PopupWindow(PhotoEditorActivity.this); + pop.setContentView(addTextPopupWindowRootView); + pop.setWidth(LinearLayout.LayoutParams.MATCH_PARENT); + pop.setHeight(LinearLayout.LayoutParams.MATCH_PARENT); + pop.setFocusable(true); + pop.setBackgroundDrawable(null); + pop.showAtLocation(addTextPopupWindowRootView, Gravity.TOP, 0, 0); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + addTextDoneTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + addText(addTextEditText.getText().toString(), colorCodeTextView); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + pop.dismiss(); + } + }); + } + + private void updateView(int visibility) { + topShadow.setVisibility(visibility); + topShadowRelativeLayout.setVisibility(visibility); + bottomShadow.setVisibility(visibility); + bottomShadowRelativeLayout.setVisibility(visibility); + } + + private void updateBrushDrawingView(boolean brushDrawingMode) { + photoEditorSDK.setBrushDrawingMode(brushDrawingMode); + if (brushDrawingMode) { + updateView(View.GONE); + drawingViewColorPickerRecyclerView.setVisibility(View.VISIBLE); + doneDrawingTextView.setVisibility(View.VISIBLE); + eraseDrawingTextView.setVisibility(View.VISIBLE); + LinearLayoutManager layoutManager = new LinearLayoutManager(PhotoEditorActivity.this, LinearLayoutManager.HORIZONTAL, false); + drawingViewColorPickerRecyclerView.setLayoutManager(layoutManager); + drawingViewColorPickerRecyclerView.setHasFixedSize(true); + ColorPickerAdapter colorPickerAdapter = new ColorPickerAdapter(PhotoEditorActivity.this, colorPickerColors); + colorPickerAdapter.setOnColorPickerClickListener(new ColorPickerAdapter.OnColorPickerClickListener() { + @Override + public void onColorPickerClickListener(int colorCode) { + photoEditorSDK.setBrushColor(colorCode); + } + }); + drawingViewColorPickerRecyclerView.setAdapter(colorPickerAdapter); + } else { + updateView(View.VISIBLE); + drawingViewColorPickerRecyclerView.setVisibility(View.GONE); + doneDrawingTextView.setVisibility(View.GONE); + eraseDrawingTextView.setVisibility(View.GONE); + } + } + + private void returnBackWithSavedImage() { + updateView(View.GONE); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); + parentImageRelativeLayout.setLayoutParams(layoutParams); + new CountDownTimer(1000, 500) { + public void onTick(long millisUntilFinished) { + + } + + public void onFinish() { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String imageName = "IMG_" + timeStamp + ".jpg"; + Intent returnIntent = new Intent(); + returnIntent.putExtra("imagePath", photoEditorSDK.saveImage("PhotoEditorSDK", imageName)); + setResult(Activity.RESULT_OK, returnIntent); + finish(); + } + }.start(); + } + + + private void returnBackWithUpdateImage() { + updateView(View.GONE); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); + parentImageRelativeLayout.setLayoutParams(layoutParams); + new CountDownTimer(1000, 500) { + public void onTick(long millisUntilFinished) { + + } + + public void onFinish() { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String imageName = "IMG_" + timeStamp + ".jpg"; + + String selectedImagePath = getIntent().getExtras().getString("selectedImagePath"); + String parsedPath = UtilFunctions.getPath(PhotoEditorActivity.this, Uri.parse(selectedImagePath)); + String uriString = null != parsedPath ? parsedPath : selectedImagePath; + File file = new File(uriString); + + try { + FileOutputStream out = new FileOutputStream(file); + if (parentImageRelativeLayout != null) { + parentImageRelativeLayout.setDrawingCacheEnabled(true); + + Bitmap bitmap = parentImageRelativeLayout.getDrawingCache(); + Bitmap rotatedBitmap = rotateBitmap(bitmap, imageOrientation, true); + rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out); + } + + out.flush(); + out.close(); + + try { + ExifInterface exifDest = new ExifInterface(file.getAbsolutePath()); + exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, Integer.toString(imageOrientation)); + exifDest.saveAttributes(); + } catch (IOException e) { + e.printStackTrace(); + } + + } catch (Exception var7) { + Log.e("onFinish URI", var7.getMessage()); + var7.printStackTrace(); + } + + Intent returnIntent = new Intent(); + returnIntent.putExtra("imagePath", uriString); + setResult(Activity.RESULT_OK, returnIntent); + + finish(); + } + }.start(); + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.close_tv) { + onBackPressed(); + } else if (v.getId() == R.id.add_image_emoji_tv) { + mLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); + } else if (v.getId() == R.id.add_text_tv) { + openAddTextPopupWindow("", -1); + } else if (v.getId() == R.id.add_pencil_tv) { + updateBrushDrawingView(true); + } else if (v.getId() == R.id.done_drawing_tv) { + updateBrushDrawingView(false); + } else if (v.getId() == R.id.save_tv || v.getId() == R.id.save_text_tv) { + returnBackWithSavedImage(); + } else if (v.getId() == R.id.clear_all_tv || v.getId() == R.id.clear_all_text_tv) { + clearAllViews(); + } else if (v.getId() == R.id.undo_text_tv || v.getId() == R.id.undo_tv) { + undoViews(); + } else if (v.getId() == R.id.erase_drawing_tv) { + eraseDrawing(); + } else if (v.getId() == R.id.go_to_next_screen_tv) { + returnBackWithUpdateImage(); + } + } + + @Override + public void onEditTextChangeListener(String text, int colorCode) { + openAddTextPopupWindow(text, colorCode); + } + + @Override + public void onAddViewListener(ViewType viewType, int numberOfAddedViews) { + if (numberOfAddedViews > 0) { + undoTextView.setVisibility(View.VISIBLE); + undoTextTextView.setVisibility(View.VISIBLE); + } + switch (viewType) { + case BRUSH_DRAWING: + Log.i("BRUSH_DRAWING", "onAddViewListener"); + break; + case EMOJI: + Log.i("EMOJI", "onAddViewListener"); + break; + case IMAGE: + Log.i("IMAGE", "onAddViewListener"); + break; + case TEXT: + Log.i("TEXT", "onAddViewListener"); + break; + } + } + + @Override + public void onRemoveViewListener(int numberOfAddedViews) { + Log.i(TAG, "onRemoveViewListener"); + if (numberOfAddedViews == 0) { + undoTextView.setVisibility(View.GONE); + undoTextTextView.setVisibility(View.GONE); + } + } + + @Override + public void onStartViewChangeListener(ViewType viewType) { + switch (viewType) { + case BRUSH_DRAWING: + Log.i("BRUSH_DRAWING", "onStartViewChangeListener"); + break; + case EMOJI: + Log.i("EMOJI", "onStartViewChangeListener"); + break; + case IMAGE: + Log.i("IMAGE", "onStartViewChangeListener"); + break; + case TEXT: + Log.i("TEXT", "onStartViewChangeListener"); + break; + } + } + + @Override + public void onStopViewChangeListener(ViewType viewType) { + switch (viewType) { + case BRUSH_DRAWING: + Log.i("BRUSH_DRAWING", "onStopViewChangeListener"); + break; + case EMOJI: + Log.i("EMOJI", "onStopViewChangeListener"); + break; + case IMAGE: + Log.i("IMAGE", "onStopViewChangeListener"); + break; + case TEXT: + Log.i("TEXT", "onStopViewChangeListener"); + break; + } + } + + private class PreviewSlidePagerAdapter extends FragmentStatePagerAdapter { + private List mFragments; + + PreviewSlidePagerAdapter(FragmentManager fm, List fragments) { + super(fm); + mFragments = fragments; + } + + @Override + public Fragment getItem(int position) { + if (mFragments == null) { + return (null); + } + return mFragments.get(position); + } + + @Override + public int getCount() { + return 2; + } + } + + private Typeface getFontFromRes(int resource) + { + Typeface tf = null; + InputStream is = null; + try { + is = getResources().openRawResource(resource); + } + catch(Resources.NotFoundException e) { + Log.e(TAG, "Could not find font in resources!"); + } + + String outPath = getCacheDir() + "/tmp" + System.currentTimeMillis() + ".raw"; + + try + { + byte[] buffer = new byte[is.available()]; + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outPath)); + + int l = 0; + while((l = is.read(buffer)) > 0) + bos.write(buffer, 0, l); + + bos.close(); + + tf = Typeface.createFromFile(outPath); + + // clean up + new File(outPath).delete(); + } + catch (IOException e) + { + Log.e(TAG, "Error reading in font!"); + return null; + } + + Log.d(TAG, "Successfully loaded font."); + + return tf; + } + + private static Bitmap rotateBitmap(Bitmap bitmap, int orientation, boolean reverse) { + Matrix matrix = new Matrix(); + + switch (orientation) { + case ExifInterface.ORIENTATION_NORMAL: + return bitmap; + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: + matrix.setScale(-1, 1); + + break; + case ExifInterface.ORIENTATION_ROTATE_180: + matrix.setRotate(180); + + break; + case ExifInterface.ORIENTATION_FLIP_VERTICAL: + matrix.setRotate(180); + matrix.postScale(-1, 1); + + break; + case ExifInterface.ORIENTATION_TRANSPOSE: + if (!reverse) { + matrix.setRotate(90); + } else { + matrix.setRotate(-90); + } + + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_90: + if (!reverse) { + matrix.setRotate(90); + } else { + matrix.setRotate(-90); + } + + break; + case ExifInterface.ORIENTATION_TRANSVERSE: + if (!reverse) { + matrix.setRotate(-90); + } else { + matrix.setRotate(90); + } + + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_270: + if (!reverse) { + matrix.setRotate(-90); + } else { + matrix.setRotate(90); + } + + break; + default: + return bitmap; + } + + try { + Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + bitmap.recycle(); + + return bmRotated; + } catch (OutOfMemoryError e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-picker-select/overrides/src/index.js b/packages/rnv/pluginTemplates/react-native-picker-select/overrides/src/index.js index 512f2180ac..b591633295 100644 --- a/packages/rnv/pluginTemplates/react-native-picker-select/overrides/src/index.js +++ b/packages/rnv/pluginTemplates/react-native-picker-select/overrides/src/index.js @@ -1,18 +1,8 @@ -import React, { PureComponent } from 'react'; -import { - ColorPropType, - Keyboard, - Modal, - Picker, - Platform, - Text, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; +import { Picker } from '@react-native-picker/picker'; +import isEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; -import isEqual from 'lodash.isequal'; +import React, { PureComponent } from 'react'; +import { Keyboard, Modal, Platform, Text, TextInput, TouchableOpacity, View } from 'react-native'; import { defaultStyles } from './styles'; export default class RNPickerSelect extends PureComponent { @@ -22,43 +12,48 @@ export default class RNPickerSelect extends PureComponent { PropTypes.shape({ label: PropTypes.string.isRequired, value: PropTypes.any.isRequired, + inputLabel: PropTypes.string, key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - color: ColorPropType, - }), + color: PropTypes.string, + }) ).isRequired, value: PropTypes.any, // eslint-disable-line react/forbid-prop-types placeholder: PropTypes.shape({ label: PropTypes.string, value: PropTypes.any, key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - color: ColorPropType, + color: PropTypes.string, }), disabled: PropTypes.bool, - showSelectedValueAsLabel: PropTypes.bool, itemKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), style: PropTypes.shape({}), children: PropTypes.any, // eslint-disable-line react/forbid-prop-types - placeholderTextColor: ColorPropType, // deprecated + onOpen: PropTypes.func, useNativeAndroidPickerStyle: PropTypes.bool, + fixAndroidTouchableBug: PropTypes.bool, // Custom Modal props (iOS only) - hideDoneBar: PropTypes.bool, // deprecated doneText: PropTypes.string, onDonePress: PropTypes.func, onUpArrow: PropTypes.func, onDownArrow: PropTypes.func, - onOpen: PropTypes.func, onClose: PropTypes.func, // Modal props (iOS only) modalProps: PropTypes.shape({}), - // TextInput props (iOS only) + // TextInput props textInputProps: PropTypes.shape({}), // Picker props pickerProps: PropTypes.shape({}), + // Touchable Done props (iOS only) + touchableDoneProps: PropTypes.shape({}), + + // Touchable wrapper props + holderProps: PropTypes.shape({}), + // Custom Icon Icon: PropTypes.func, InputAccessoryView: PropTypes.func, @@ -75,9 +70,8 @@ export default class RNPickerSelect extends PureComponent { itemKey: null, style: {}, children: null, - placeholderTextColor: '#C7C7CD', // deprecated useNativeAndroidPickerStyle: true, - hideDoneBar: false, // deprecated + fixAndroidTouchableBug: false, doneText: 'Done', onDonePress: null, onUpArrow: null, @@ -87,9 +81,10 @@ export default class RNPickerSelect extends PureComponent { modalProps: {}, textInputProps: {}, pickerProps: {}, + touchableDoneProps: {}, + holderProps: {}, Icon: null, InputAccessoryView: null, - showSelectedValueAsLabel: true, }; static handlePlaceholder({ placeholder }) { @@ -115,46 +110,17 @@ export default class RNPickerSelect extends PureComponent { }; } - static getDerivedStateFromProps(nextProps, prevState) { - // update items if items or placeholder prop changes - const items = RNPickerSelect.handlePlaceholder({ - placeholder: nextProps.placeholder, - }).concat(nextProps.items); - const itemsChanged = !isEqual(prevState.items, items); - - // update selectedItem if value prop is defined and differs from currently selected item - const { selectedItem, idx } = RNPickerSelect.getSelectedItem({ - items, - key: nextProps.itemKey, - value: nextProps.value, - }); - const selectedItemChanged = !isEqual(nextProps.value, undefined) && !isEqual(prevState.selectedItem, selectedItem); - - if (itemsChanged || selectedItemChanged) { - if (selectedItemChanged) { - nextProps.onValueChange(selectedItem.value, idx); - } - - return { - ...(itemsChanged ? { items } : {}), - ...(selectedItemChanged ? { selectedItem } : {}), - }; - } - - return null; - } - constructor(props) { super(props); const items = RNPickerSelect.handlePlaceholder({ - placeholder: this.props.placeholder, - }).concat(this.props.items); + placeholder: props.placeholder, + }).concat(props.items); const { selectedItem } = RNPickerSelect.getSelectedItem({ items, - key: this.props.itemKey, - value: this.props.value, + key: props.itemKey, + value: props.value, }); this.state = { @@ -163,6 +129,7 @@ export default class RNPickerSelect extends PureComponent { showPicker: false, animationType: undefined, orientation: 'portrait', + doneDepressed: false, }; this.onUpArrow = this.onUpArrow.bind(this); @@ -171,10 +138,34 @@ export default class RNPickerSelect extends PureComponent { this.onOrientationChange = this.onOrientationChange.bind(this); this.setInputRef = this.setInputRef.bind(this); this.togglePicker = this.togglePicker.bind(this); - this.triggerDoneCallback = this.triggerDoneCallback.bind(this); this.renderInputAccessoryView = this.renderInputAccessoryView.bind(this); } + componentDidUpdate = (prevProps, prevState) => { + // update items if items or placeholder prop changes + const items = RNPickerSelect.handlePlaceholder({ + placeholder: this.props.placeholder, + }).concat(this.props.items); + const itemsChanged = !isEqual(prevState.items, items); + + // update selectedItem if value prop is defined and differs from currently selected item + const { selectedItem, idx } = RNPickerSelect.getSelectedItem({ + items, + key: this.props.itemKey, + value: this.props.value, + }); + const selectedItemChanged = !isEqual(this.props.value, undefined) && !isEqual(prevState.selectedItem, selectedItem); + + if (itemsChanged || selectedItemChanged) { + this.props.onValueChange(selectedItem.value, idx); + + this.setState({ + ...(itemsChanged ? { items } : {}), + ...(selectedItemChanged ? { selectedItem } : {}), + }); + } + }; + onUpArrow() { const { onUpArrow } = this.props; @@ -208,12 +199,12 @@ export default class RNPickerSelect extends PureComponent { } getPlaceholderStyle() { - const { placeholder, placeholderTextColor, style } = this.props; + const { placeholder, style } = this.props; + const { selectedItem } = this.state; - if (!isEqual(placeholder, {}) && this.state.selectedItem.label === placeholder.label) { + if (!isEqual(placeholder, {}) && selectedItem.label === placeholder.label) { return { ...defaultStyles.placeholder, - color: placeholderTextColor, // deprecated ...style.placeholder, }; } @@ -222,31 +213,26 @@ export default class RNPickerSelect extends PureComponent { triggerOpenCloseCallbacks() { const { onOpen, onClose } = this.props; + const { showPicker } = this.state; - if (!this.state.showPicker && onOpen) { + if (!showPicker && onOpen) { onOpen(); } - if (this.state.showPicker && onClose) { + if (showPicker && onClose) { onClose(); } } - triggerDoneCallback() { - const { hideDoneBar, onDonePress } = this.props; - if (!hideDoneBar && onDonePress) { - onDonePress(); - } - } - togglePicker(animate = false, postToggleCallback) { const { modalProps, disabled } = this.props; + const { showPicker } = this.state; if (disabled) { return; } - if (!this.state.showPicker) { + if (!showPicker) { Keyboard.dismiss(); } @@ -263,12 +249,14 @@ export default class RNPickerSelect extends PureComponent { if (postToggleCallback) { postToggleCallback(); } - }, + } ); } renderPickerItems() { - return this.state.items.map(item => ( + const { items } = this.state; + + return items.map(item => ( ; @@ -334,19 +321,36 @@ export default class RNPickerSelect extends PureComponent { /> - { - this.togglePicker(true); + this.togglePicker(true, onDonePress); + }} + onPressIn={() => { + this.setState({ doneDepressed: true }); + }} + onPressOut={() => { + this.setState({ doneDepressed: false }); }} hitSlop={{ top: 4, right: 4, bottom: 4, left: 4 }} - testID="done_button" + {...touchableDoneProps} > - + {doneText} - + ); } @@ -369,7 +373,9 @@ export default class RNPickerSelect extends PureComponent { } renderTextInputOrChildren() { - const { children, style, textInputProps, showSelectedValueAsLabel } = this.props; + const { children, style, textInputProps, testID } = this.props; + const { selectedItem } = this.state; + const containerStyle = Platform.OS === 'ios' ? style.inputIOSContainer : style.inputAndroidContainer; if (children) { @@ -383,11 +389,12 @@ export default class RNPickerSelect extends PureComponent { return ( - + { this.togglePicker(true); }} - testID="ios_touchable_wrapper" + activeOpacity={1} > {this.renderTextInputOrChildren()} - + @@ -431,14 +439,14 @@ export default class RNPickerSelect extends PureComponent { {this.renderPickerItems()} @@ -450,44 +458,83 @@ export default class RNPickerSelect extends PureComponent { } renderAndroidHeadless() { - const { disabled, Icon, style, pickerProps } = this.props; + const { + disabled, + style, + pickerProps, + onOpen, + holderProps, + fixAndroidTouchableBug, + testID + } = this.props; + const { selectedItem } = this.state; + const Component = fixAndroidTouchableBug ? View : TouchableOpacity; return ( - - {this.renderTextInputOrChildren()} + + + {this.renderTextInputOrChildren()} + + {this.renderPickerItems()} + + + + ); + } + + renderAndroidNativePickerStyle() { + const { disabled, Icon, style, pickerProps, holderProps } = this.props; + const { selectedItem } = this.state; + + return ( + {this.renderPickerItems()} + {this.renderIcon()} ); } - renderAndroidNativePickerStyle() { - const { disabled, Icon, style, pickerProps } = this.props; + renderWeb() { + const { disabled, style, pickerProps, holderProps } = this.props; + const { selectedItem } = this.state; return ( - + {this.renderPickerItems()} @@ -504,6 +551,10 @@ export default class RNPickerSelect extends PureComponent { return this.renderIOS(); } + if (Platform.OS === 'web') { + return this.renderWeb(); + } + if (children || !useNativeAndroidPickerStyle) { return this.renderAndroidHeadless(); } diff --git a/packages/rnv/pluginTemplates/react-native-picker-select/overrides@6.3.3/src/index.js b/packages/rnv/pluginTemplates/react-native-picker-select/overrides@6.3.3/src/index.js new file mode 100644 index 0000000000..512f2180ac --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-picker-select/overrides@6.3.3/src/index.js @@ -0,0 +1,515 @@ +import React, { PureComponent } from 'react'; +import { + ColorPropType, + Keyboard, + Modal, + Picker, + Platform, + Text, + TextInput, + TouchableOpacity, + TouchableWithoutFeedback, + View, +} from 'react-native'; +import PropTypes from 'prop-types'; +import isEqual from 'lodash.isequal'; +import { defaultStyles } from './styles'; + +export default class RNPickerSelect extends PureComponent { + static propTypes = { + onValueChange: PropTypes.func.isRequired, + items: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.any.isRequired, + key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + color: ColorPropType, + }), + ).isRequired, + value: PropTypes.any, // eslint-disable-line react/forbid-prop-types + placeholder: PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.any, + key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + color: ColorPropType, + }), + disabled: PropTypes.bool, + showSelectedValueAsLabel: PropTypes.bool, + itemKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + style: PropTypes.shape({}), + children: PropTypes.any, // eslint-disable-line react/forbid-prop-types + placeholderTextColor: ColorPropType, // deprecated + useNativeAndroidPickerStyle: PropTypes.bool, + + // Custom Modal props (iOS only) + hideDoneBar: PropTypes.bool, // deprecated + doneText: PropTypes.string, + onDonePress: PropTypes.func, + onUpArrow: PropTypes.func, + onDownArrow: PropTypes.func, + onOpen: PropTypes.func, + onClose: PropTypes.func, + + // Modal props (iOS only) + modalProps: PropTypes.shape({}), + + // TextInput props (iOS only) + textInputProps: PropTypes.shape({}), + + // Picker props + pickerProps: PropTypes.shape({}), + + // Custom Icon + Icon: PropTypes.func, + InputAccessoryView: PropTypes.func, + }; + + static defaultProps = { + value: undefined, + placeholder: { + label: 'Select an item...', + value: null, + color: '#9EA0A4', + }, + disabled: false, + itemKey: null, + style: {}, + children: null, + placeholderTextColor: '#C7C7CD', // deprecated + useNativeAndroidPickerStyle: true, + hideDoneBar: false, // deprecated + doneText: 'Done', + onDonePress: null, + onUpArrow: null, + onDownArrow: null, + onOpen: null, + onClose: null, + modalProps: {}, + textInputProps: {}, + pickerProps: {}, + Icon: null, + InputAccessoryView: null, + showSelectedValueAsLabel: true, + }; + + static handlePlaceholder({ placeholder }) { + if (isEqual(placeholder, {})) { + return []; + } + return [placeholder]; + } + + static getSelectedItem({ items, key, value }) { + let idx = items.findIndex((item) => { + if (item.key && key) { + return isEqual(item.key, key); + } + return isEqual(item.value, value); + }); + if (idx === -1) { + idx = 0; + } + return { + selectedItem: items[idx] || {}, + idx, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) { + // update items if items or placeholder prop changes + const items = RNPickerSelect.handlePlaceholder({ + placeholder: nextProps.placeholder, + }).concat(nextProps.items); + const itemsChanged = !isEqual(prevState.items, items); + + // update selectedItem if value prop is defined and differs from currently selected item + const { selectedItem, idx } = RNPickerSelect.getSelectedItem({ + items, + key: nextProps.itemKey, + value: nextProps.value, + }); + const selectedItemChanged = !isEqual(nextProps.value, undefined) && !isEqual(prevState.selectedItem, selectedItem); + + if (itemsChanged || selectedItemChanged) { + if (selectedItemChanged) { + nextProps.onValueChange(selectedItem.value, idx); + } + + return { + ...(itemsChanged ? { items } : {}), + ...(selectedItemChanged ? { selectedItem } : {}), + }; + } + + return null; + } + + constructor(props) { + super(props); + + const items = RNPickerSelect.handlePlaceholder({ + placeholder: this.props.placeholder, + }).concat(this.props.items); + + const { selectedItem } = RNPickerSelect.getSelectedItem({ + items, + key: this.props.itemKey, + value: this.props.value, + }); + + this.state = { + items, + selectedItem, + showPicker: false, + animationType: undefined, + orientation: 'portrait', + }; + + this.onUpArrow = this.onUpArrow.bind(this); + this.onDownArrow = this.onDownArrow.bind(this); + this.onValueChange = this.onValueChange.bind(this); + this.onOrientationChange = this.onOrientationChange.bind(this); + this.setInputRef = this.setInputRef.bind(this); + this.togglePicker = this.togglePicker.bind(this); + this.triggerDoneCallback = this.triggerDoneCallback.bind(this); + this.renderInputAccessoryView = this.renderInputAccessoryView.bind(this); + } + + onUpArrow() { + const { onUpArrow } = this.props; + + this.togglePicker(false, onUpArrow); + } + + onDownArrow() { + const { onDownArrow } = this.props; + + this.togglePicker(false, onDownArrow); + } + + onValueChange(value, index) { + const { onValueChange } = this.props; + + onValueChange(value, index); + + this.setState(prevState => ({ + selectedItem: prevState.items[index], + })); + } + + onOrientationChange({ nativeEvent }) { + this.setState({ + orientation: nativeEvent.orientation, + }); + } + + setInputRef(ref) { + this.inputRef = ref; + } + + getPlaceholderStyle() { + const { placeholder, placeholderTextColor, style } = this.props; + + if (!isEqual(placeholder, {}) && this.state.selectedItem.label === placeholder.label) { + return { + ...defaultStyles.placeholder, + color: placeholderTextColor, // deprecated + ...style.placeholder, + }; + } + return {}; + } + + triggerOpenCloseCallbacks() { + const { onOpen, onClose } = this.props; + + if (!this.state.showPicker && onOpen) { + onOpen(); + } + + if (this.state.showPicker && onClose) { + onClose(); + } + } + + triggerDoneCallback() { + const { hideDoneBar, onDonePress } = this.props; + if (!hideDoneBar && onDonePress) { + onDonePress(); + } + } + + togglePicker(animate = false, postToggleCallback) { + const { modalProps, disabled } = this.props; + + if (disabled) { + return; + } + + if (!this.state.showPicker) { + Keyboard.dismiss(); + } + + const animationType = modalProps && modalProps.animationType ? modalProps.animationType : 'slide'; + + this.triggerOpenCloseCallbacks(); + + this.setState( + prevState => ({ + animationType: animate ? animationType : undefined, + showPicker: !prevState.showPicker, + }), + () => { + if (postToggleCallback) { + postToggleCallback(); + } + }, + ); + } + + renderPickerItems() { + return this.state.items.map(item => ( + + )); + } + + renderInputAccessoryView() { + const { + InputAccessoryView, + doneText, + hideDoneBar, + onUpArrow, + onDownArrow, + style, + } = this.props; + + // deprecated + if (hideDoneBar) { + return null; + } + + if (InputAccessoryView) { + return ; + } + + return ( + + + + + + + + + + { + this.togglePicker(true); + }} + hitSlop={{ top: 4, right: 4, bottom: 4, left: 4 }} + testID="done_button" + > + + + {doneText} + + + + + ); + } + + renderIcon() { + const { style, Icon } = this.props; + + if (!Icon) { + return null; + } + + return ( + + + + ); + } + + renderTextInputOrChildren() { + const { children, style, textInputProps, showSelectedValueAsLabel } = this.props; + const containerStyle = Platform.OS === 'ios' ? style.inputIOSContainer : style.inputAndroidContainer; + + if (children) { + return ( + + {children} + + ); + } + + return ( + + + {this.renderIcon()} + + ); + } + + renderIOS() { + const { style, modalProps, pickerProps } = this.props; + + return ( + + { + this.togglePicker(true); + }} + testID="ios_touchable_wrapper" + > + {this.renderTextInputOrChildren()} + + + { + this.togglePicker(true); + }} + /> + {this.renderInputAccessoryView()} + + + {this.renderPickerItems()} + + + + + ); + } + + renderAndroidHeadless() { + const { disabled, Icon, style, pickerProps } = this.props; + + return ( + + {this.renderTextInputOrChildren()} + + {this.renderPickerItems()} + + + ); + } + + renderAndroidNativePickerStyle() { + const { disabled, Icon, style, pickerProps } = this.props; + + return ( + + + {this.renderPickerItems()} + + {this.renderIcon()} + + ); + } + + render() { + const { children, useNativeAndroidPickerStyle } = this.props; + + if (Platform.OS === 'ios') { + return this.renderIOS(); + } + + if (children || !useNativeAndroidPickerStyle) { + return this.renderAndroidHeadless(); + } + + return this.renderAndroidNativePickerStyle(); + } +} + +export { defaultStyles }; diff --git a/packages/rnv/pluginTemplates/react-native-prompt-android/overrides/android/src/main/java/im/shimo/react/prompt/RNPromptFragment.java b/packages/rnv/pluginTemplates/react-native-prompt-android/overrides/android/src/main/java/im/shimo/react/prompt/RNPromptFragment.java new file mode 100644 index 0000000000..f1f3412ca9 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-prompt-android/overrides/android/src/main/java/im/shimo/react/prompt/RNPromptFragment.java @@ -0,0 +1,180 @@ +package im.shimo.react.prompt; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import androidx.appcompat.app.AlertDialog; +import android.text.InputType; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.WindowManager; +import android.widget.EditText; + +import javax.annotation.Nullable; + +public class RNPromptFragment extends DialogFragment implements DialogInterface.OnClickListener { + + /* package */ static final String ARG_TITLE = "title"; + /* package */ static final String ARG_MESSAGE = "message"; + /* package */ static final String ARG_BUTTON_POSITIVE = "button_positive"; + /* package */ static final String ARG_BUTTON_NEGATIVE = "button_negative"; + /* package */ static final String ARG_BUTTON_NEUTRAL = "button_neutral"; + /* package */ static final String ARG_ITEMS = "items"; + /* package */ static final String ARG_TYPE = "type"; + /* package */ static final String ARG_STYLE = "style"; + /* package */ static final String ARG_DEFAULT_VALUE = "defaultValue"; + /* package */ static final String ARG_PLACEHOLDER = "placeholder"; + + private EditText mInputText; + + public enum PromptTypes { + TYPE_DEFAULT("default"), + PLAIN_TEXT("plain-text"), + SECURE_TEXT("secure-text"), + NUMERIC("numeric"), + EMAIL_ADDRESS("email-address"), + PHONE_PAD("phone-pad"); + + private final String mName; + + PromptTypes(final String name) { + mName = name; + } + + @Override + public String toString() { + return mName; + } + } + + private + @Nullable + RNPromptModule.PromptFragmentListener mListener; + + public RNPromptFragment() { + mListener = null; + } + + public void setListener(@Nullable RNPromptModule.PromptFragmentListener listener) { + mListener = listener; + } + + public Dialog createDialog(Context activityContext, Bundle arguments) { + AlertDialog.Builder builder; + String style = arguments.containsKey(ARG_STYLE) ? arguments.getString(ARG_STYLE) : "default"; + style = style != null ? style : "default"; + + // AlertDialog style + switch (style) { + case "shimo": + builder = new AlertDialog.Builder(activityContext, R.style.ShimoAlertDialogStyle); + break; + default: + builder = new AlertDialog.Builder(activityContext); + } + + builder.setTitle(arguments.getString(ARG_TITLE)); + + if (arguments.containsKey(ARG_BUTTON_POSITIVE)) { + builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), this); + } + if (arguments.containsKey(ARG_BUTTON_NEGATIVE)) { + builder.setNegativeButton(arguments.getString(ARG_BUTTON_NEGATIVE), this); + } + if (arguments.containsKey(ARG_BUTTON_NEUTRAL)) { + builder.setNeutralButton(arguments.getString(ARG_BUTTON_NEUTRAL), this); + } + // if both message and items are set, Android will only show the message + // and ignore the items argument entirely + if (arguments.containsKey(ARG_MESSAGE)) { + builder.setMessage(arguments.getString(ARG_MESSAGE)); + } + + if (arguments.containsKey(ARG_ITEMS)) { + builder.setItems(arguments.getCharSequenceArray(ARG_ITEMS), this); + } + + AlertDialog alertDialog = builder.create(); + + // input style + LayoutInflater inflater = LayoutInflater.from(activityContext); + final EditText input; + switch (style) { + case "shimo": + input = (EditText) inflater.inflate(R.layout.edit_text, null); + break; + default: + input = new EditText(activityContext); + } + + // input type + int type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + if (arguments.containsKey(ARG_TYPE)) { + String typeString = arguments.getString(ARG_TYPE); + if (typeString != null) { + switch (typeString) { + case "secure-text": + type = InputType.TYPE_TEXT_VARIATION_PASSWORD; + break; + case "numeric": + type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_CLASS_NUMBER; + break; + case "email-address": + type = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; + break; + case "phone-pad": + type = InputType.TYPE_CLASS_PHONE; + break; + case "plain-text": + default: + type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + } + } + + input.setInputType(type); + + if (arguments.containsKey(ARG_DEFAULT_VALUE)) { + String defaultValue = arguments.getString(ARG_DEFAULT_VALUE); + if (defaultValue != null) { + input.setText(defaultValue); + int textLength = input.getText().length(); + input.setSelection(textLength, textLength); + } + } + + if (arguments.containsKey(ARG_PLACEHOLDER)) { + input.setHint(arguments.getString(ARG_PLACEHOLDER)); + } + alertDialog.setView(input, 50, 15, 50, 0); + + mInputText = input; + return alertDialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = this.createDialog(getActivity(), getArguments()); + if (mInputText.requestFocus()) { + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } + return dialog; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mListener != null) { + mListener.onConfirm(which, mInputText.getText().toString()); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (mListener != null) { + mListener.onDismiss(dialog); + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-reanimated/overrides/RNReanimated.podspec b/packages/rnv/pluginTemplates/react-native-reanimated/overrides/RNReanimated.podspec index cce37df608..e6e2b19bb6 100644 --- a/packages/rnv/pluginTemplates/react-native-reanimated/overrides/RNReanimated.podspec +++ b/packages/rnv/pluginTemplates/react-native-reanimated/overrides/RNReanimated.podspec @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m}" s.requires_arc = true - s.dependency "React" + s.dependency "React-Core" end diff --git a/packages/rnv/pluginTemplates/react-native-screens/overrides/RNScreens.podspec b/packages/rnv/pluginTemplates/react-native-screens/overrides/RNScreens.podspec index d1875b8b3f..b388a744d2 100644 --- a/packages/rnv/pluginTemplates/react-native-screens/overrides/RNScreens.podspec +++ b/packages/rnv/pluginTemplates/react-native-screens/overrides/RNScreens.podspec @@ -2,6 +2,10 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' + +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + Pod::Spec.new do |s| s.name = "RNScreens" s.version = package["version"] @@ -9,15 +13,41 @@ Pod::Spec.new do |s| s.description = <<-DESC RNScreens - first incomplete navigation solution for your React Native app DESC - s.homepage = "https://github.com/kmagiera/react-native-screens" + s.homepage = "https://github.com/software-mansion/react-native-screens" s.license = "MIT" # s.license = { :type => "MIT", :file => "FILE_LICENSE" } s.author = { "author" => "author@domain.cn" } - s.platforms = { :ios => "7.0", :tvos => "9.2" } - s.source = { :git => "https://github.com/kmagiera/react-native-screens.git", :tag => "#{s.version}" } - - s.source_files = "ios/**/*.{h,m}" - s.requires_arc = true + s.platforms = { :ios => "9.0", :tvos => "11.0" } + s.source = { :git => "https://github.com/software-mansion/react-native-screens.git", :tag => "#{s.version}" } - s.dependency "React" -end + if fabric_enabled + s.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"', + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", + } + s.platforms = { ios: '11.0', tvos: '11.0' } + s.compiler_flags = folly_compiler_flags + ' ' + '-DRCT_NEW_ARCH_ENABLED' + s.source_files = 'ios/**/*.{h,m,mm,cpp}' + s.requires_arc = true + + s.dependency "React-Core" + s.dependency "React-RCTFabric" + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + + s.subspec "common" do |ss| + ss.source_files = "common/cpp/**/*.{cpp,h}" + ss.header_dir = "rnscreens" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/common/cpp\"" } + end + else + s.source_files = "ios/**/*.{h,m,mm}" + s.requires_arc = true + + s.dependency "React-Core" + s.dependency "React-RCTImage" + end +end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/build.gradle new file mode 100644 index 0000000000..650976a2d5 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/build.gradle @@ -0,0 +1,27 @@ +// Override rn to 0.20.0 in order to have never okhttp + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } +} + +dependencies { + compile 'com.android.support:appcompat-v7:23.0.0' + compile ('com.facebook.react:react-native:+') { + exclude group: 'com.facebook.fresco', module: 'imagepipeline-okhttp' + exclude group: 'com.squareup.okhttp', module: 'okhttp' + exclude group: 'com.squareup.okhttp', module: 'okhttp-ws' + } +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureMainView.java b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureMainView.java new file mode 100644 index 0000000000..e64c4b0f70 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureMainView.java @@ -0,0 +1,212 @@ +package com.rssignaturecapture; + +import android.util.Log; +import android.view.ViewGroup; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.uimanager.ThemedReactContext; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.events.RCTEventEmitter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; + +import android.util.Base64; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.LinearLayout; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import java.lang.Boolean; +import java.io.IOException; + +public class RSSignatureCaptureMainView extends LinearLayout implements OnClickListener,RSSignatureCaptureView.SignatureCallback { + LinearLayout buttonsLayout; + RSSignatureCaptureView signatureView; + + Activity mActivity; + int mOriginalOrientation; + Boolean saveFileInExtStorage = false; + String viewMode = "portrait"; + Boolean showBorder = true; + Boolean showNativeButtons = true; + Boolean showTitleLabel = true; + int maxSize = 500; + + public RSSignatureCaptureMainView(Context context, Activity activity) { + super(context); + Log.d("React:", "RSSignatureCaptureMainView(Contructtor)"); + mOriginalOrientation = activity.getRequestedOrientation(); + mActivity = activity; + + this.setOrientation(LinearLayout.VERTICAL); + this.signatureView = new RSSignatureCaptureView(context,this); + // add the buttons and signature views + this.buttonsLayout = this.buttonsLayout(); + this.addView(this.buttonsLayout); + this.addView(signatureView); + + setLayoutParams(new android.view.ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + } + + public RSSignatureCaptureView getSignatureView() { + return signatureView; + } + + public void setSaveFileInExtStorage(Boolean saveFileInExtStorage) { + this.saveFileInExtStorage = saveFileInExtStorage; + } + + public void setViewMode(String viewMode) { + this.viewMode = viewMode; + + if (viewMode.equalsIgnoreCase("portrait")) { + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } else if (viewMode.equalsIgnoreCase("landscape")) { + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + } + + public void setShowNativeButtons(Boolean showNativeButtons) { + this.showNativeButtons = showNativeButtons; + if (showNativeButtons) { + Log.d("Added Native Buttons", "Native Buttons:" + showNativeButtons); + buttonsLayout.setVisibility(View.VISIBLE); + } else { + buttonsLayout.setVisibility(View.GONE); + } + } + + public void setMaxSize(int size) { + this.maxSize = size; + } + + + private LinearLayout buttonsLayout() { + + // create the UI programatically + LinearLayout linearLayout = new LinearLayout(this.getContext()); + Button saveBtn = new Button(this.getContext()); + Button clearBtn = new Button(this.getContext()); + + // set orientation + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.setBackgroundColor(Color.WHITE); + + // set texts, tags and OnClickListener + saveBtn.setText("Save"); + saveBtn.setTag("Save"); + saveBtn.setOnClickListener(this); + + clearBtn.setText("Reset"); + clearBtn.setTag("Reset"); + clearBtn.setOnClickListener(this); + + linearLayout.addView(saveBtn); + linearLayout.addView(clearBtn); + + // return the whoe layout + return linearLayout; + } + + // the on click listener of 'save' and 'clear' buttons + @Override public void onClick(View v) { + String tag = v.getTag().toString().trim(); + + // save the signature + if (tag.equalsIgnoreCase("save")) { + try { + this.saveImage(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // empty the canvas + else if (tag.equalsIgnoreCase("Reset")) { + this.signatureView.clearSignature(); + } + } + + /** + * save the signature to an sd card directory + */ + final void saveImage() throws IOException { + File outputDir = this.getContext().getCacheDir(); + Long tsLong = System.currentTimeMillis()/1000; + String ts = tsLong.toString(); + File file = File.createTempFile("signature" + ts, ".png", outputDir); + + if (file.exists()) { + file.delete(); + } + + try { + + Log.d("React Signature", "Save file-======:" + saveFileInExtStorage); + // save the signature + FileOutputStream out = new FileOutputStream(file); + this.signatureView.getSignature().compress(Bitmap.CompressFormat.PNG, 90, out); + out.flush(); + out.close(); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Bitmap resizedBitmap = getResizedBitmap(this.signatureView.getSignature()); + resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); + + + byte[] byteArray = byteArrayOutputStream.toByteArray(); + String encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP); + + WritableMap event = Arguments.createMap(); + event.putString("pathName", file.getAbsolutePath()); + event.putString("encoded", encoded); + ReactContext reactContext = (ReactContext) getContext(); + reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topChange", event); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public Bitmap getResizedBitmap(Bitmap image) { + Log.d("React Signature","maxSize:"+maxSize); + int width = image.getWidth(); + int height = image.getHeight(); + + float bitmapRatio = (float) width / (float) height; + if (bitmapRatio > 1) { + width = maxSize; + height = (int) (width / bitmapRatio); + } else { + height = maxSize; + width = (int) (height * bitmapRatio); + } + + return Bitmap.createScaledBitmap(image, width, height, true); + } + + + public void reset() { + if (this.signatureView != null) { + this.signatureView.clearSignature(); + } + } + + @Override public void onDragged() { + WritableMap event = Arguments.createMap(); + event.putBoolean("dragged", true); + ReactContext reactContext = (ReactContext) getContext(); + reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topChange", event); + + } +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureView.java b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureView.java new file mode 100644 index 0000000000..9cd4641204 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureView.java @@ -0,0 +1,382 @@ +package com.rssignaturecapture; + +import android.content.Context; + +import android.content.res.Resources; +import android.content.res.TypedArray; + +import android.util.Log; +import android.view.View; +import android.view.MotionEvent; + +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Canvas; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.RectF; + +import android.util.DisplayMetrics; + +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +import java.util.ArrayList; +import java.util.List; + +import com.rssignaturecapture.utils.TimedPoint; +import com.rssignaturecapture.utils.ControlTimedPoints; +import com.rssignaturecapture.utils.Bezier; + +public class RSSignatureCaptureView extends View { + private static final float STROKE_WIDTH = 5f; + private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2; + + private boolean mIsEmpty; + private OnSignedListener mOnSignedListener; + private int mMinWidth; + private int mMaxWidth; + private float mLastTouchX; + private float mLastTouchY; + private float mLastVelocity; + private float mLastWidth; + private RectF mDirtyRect; + + private List mPoints; + private Paint mPaint = new Paint(); + private Path mPath = new Path(); + private Bitmap mSignatureBitmap = null; + + private float mVelocityFilterWeight; + private Canvas mSignatureBitmapCanvas = null; + private SignatureCallback callback; + private boolean dragged = false; + private boolean multipleTouchDragged = false; + private int SCROLL_THRESHOLD = 5; + + public interface SignatureCallback { + void onDragged(); + } + + public RSSignatureCaptureView(Context context, SignatureCallback callback) { + + super(context); + this.callback = callback; + + //Fixed parameters + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setStrokeJoin(Paint.Join.ROUND); + + mMinWidth = convertDpToPx(4); + mMaxWidth = convertDpToPx(8); + mVelocityFilterWeight = 0.4f; + mPaint.setColor(Color.BLACK); + + //Dirty rectangle to update only the changed portion of the view + mDirtyRect = new RectF(); + + clear(); + + // set the bg color as white + this.setBackgroundColor(Color.WHITE); + + // width and height should cover the screen + this.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + } + + /** + * Get signature + * + * @return + */ + public Bitmap getSignature() { + + Bitmap signatureBitmap = null; + + // set the signature bitmap + if (signatureBitmap == null) { + signatureBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888); + } + + // important for saving signature + final Canvas canvas = new Canvas(signatureBitmap); + this.draw(canvas); + + return signatureBitmap; + } + + + /** + * clear signature canvas + */ + public void clearSignature() { + clear(); + } + + private void addPoint(TimedPoint newPoint) { + mPoints.add(newPoint); + if (mPoints.size() > 2) { + // To reduce the initial lag make it work with 3 mPoints + // by copying the first point to the beginning. + if (mPoints.size() == 3) mPoints.add(0, mPoints.get(0)); + + ControlTimedPoints tmp = calculateCurveControlPoints(mPoints.get(0), mPoints.get(1), mPoints.get(2)); + TimedPoint c2 = tmp.c2; + tmp = calculateCurveControlPoints(mPoints.get(1), mPoints.get(2), mPoints.get(3)); + TimedPoint c3 = tmp.c1; + Bezier curve = new Bezier(mPoints.get(1), c2, c3, mPoints.get(2)); + + TimedPoint startPoint = curve.startPoint; + TimedPoint endPoint = curve.endPoint; + + float velocity = endPoint.velocityFrom(startPoint); + velocity = Float.isNaN(velocity) ? 0.0f : velocity; + + velocity = mVelocityFilterWeight * velocity + + (1 - mVelocityFilterWeight) * mLastVelocity; + + // The new width is a function of the velocity. Higher velocities + // correspond to thinner strokes. + float newWidth = strokeWidth(velocity); + + // The Bezier's width starts out as last curve's final width, and + // gradually changes to the stroke width just calculated. The new + // width calculation is based on the velocity between the Bezier's + // start and end mPoints. + addBezier(curve, mLastWidth, newWidth); + + mLastVelocity = velocity; + mLastWidth = newWidth; + + // Remove the first element from the list, + // so that we always have no more than 4 mPoints in mPoints array. + mPoints.remove(0); + } + } + + private void addBezier(Bezier curve, float startWidth, float endWidth) { + ensureSignatureBitmap(); + float originalWidth = mPaint.getStrokeWidth(); + float widthDelta = endWidth - startWidth; + float drawSteps = (float) Math.floor(curve.length()); + + for (int i = 0; i < drawSteps; i++) { + // Calculate the Bezier (x, y) coordinate for this step. + float t = ((float) i) / drawSteps; + float tt = t * t; + float ttt = tt * t; + float u = 1 - t; + float uu = u * u; + float uuu = uu * u; + + float x = uuu * curve.startPoint.x; + x += 3 * uu * t * curve.control1.x; + x += 3 * u * tt * curve.control2.x; + x += ttt * curve.endPoint.x; + + float y = uuu * curve.startPoint.y; + y += 3 * uu * t * curve.control1.y; + y += 3 * u * tt * curve.control2.y; + y += ttt * curve.endPoint.y; + + // Set the incremental stroke width and draw. + mPaint.setStrokeWidth(startWidth + ttt * widthDelta); + mSignatureBitmapCanvas.drawPoint(x, y, mPaint); + expandDirtyRect(x, y); + } + + mPaint.setStrokeWidth(originalWidth); + } + + private void ensureSignatureBitmap() { + if (mSignatureBitmap == null) { + mSignatureBitmap = Bitmap.createBitmap(getWidth(), getHeight(), + Bitmap.Config.ARGB_8888); + mSignatureBitmapCanvas = new Canvas(mSignatureBitmap); + } + } + + public void setMinStrokeWidth(int minStrokeWidth) { + mMinWidth = minStrokeWidth; + } + + public void setMaxStrokeWidth(int maxStrokeWidth) { + mMaxWidth = maxStrokeWidth; + } + + public void setStrokeColor(int color) { + mPaint.setColor(color); + } + + private float strokeWidth(float velocity) { + return Math.max(mMaxWidth / (velocity + 1), mMinWidth); + } + + private ControlTimedPoints calculateCurveControlPoints(TimedPoint s1, TimedPoint s2, TimedPoint s3) { + float dx1 = s1.x - s2.x; + float dy1 = s1.y - s2.y; + float dx2 = s2.x - s3.x; + float dy2 = s2.y - s3.y; + + TimedPoint m1 = new TimedPoint((s1.x + s2.x) / 2.0f, (s1.y + s2.y) / 2.0f); + TimedPoint m2 = new TimedPoint((s2.x + s3.x) / 2.0f, (s2.y + s3.y) / 2.0f); + + float l1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1); + float l2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2); + + float dxm = (m1.x - m2.x); + float dym = (m1.y - m2.y); + float k = l2 / (l1 + l2); + TimedPoint cm = new TimedPoint(m2.x + dxm * k, m2.y + dym * k); + + float tx = s2.x - cm.x; + float ty = s2.y - cm.y; + + return new ControlTimedPoints(new TimedPoint(m1.x + tx, m1.y + ty), new TimedPoint(m2.x + tx, m2.y + ty)); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isEnabled() || event.getPointerCount() > 1 || (multipleTouchDragged && event.getAction() != MotionEvent.ACTION_UP)) { + multipleTouchDragged = true; + return false; + } + + float eventX = event.getX(); + float eventY = event.getY(); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mLastTouchX = eventX; + mLastTouchY = eventY; + getParent().requestDisallowInterceptTouchEvent(true); + mPoints.clear(); + mPath.moveTo(eventX, eventY); + addPoint(new TimedPoint(eventX, eventY)); + + case MotionEvent.ACTION_MOVE: + if((Math.abs(mLastTouchX - eventX) < SCROLL_THRESHOLD || Math.abs(mLastTouchY - eventY) < SCROLL_THRESHOLD) && dragged) { + return false; + } + resetDirtyRect(eventX, eventY); + addPoint(new TimedPoint(eventX, eventY)); + dragged = true; + break; + + case MotionEvent.ACTION_UP: + if(mPoints.size() >= 3) { + resetDirtyRect(eventX, eventY); + addPoint(new TimedPoint(eventX, eventY)); + getParent().requestDisallowInterceptTouchEvent(true); + setIsEmpty(false); + sendDragEventToReact(); + } + dragged = false; + multipleTouchDragged = false; + break; + + default: + return false; + } + + //invalidate(); + invalidate( + (int) (mDirtyRect.left - mMaxWidth), + (int) (mDirtyRect.top - mMaxWidth), + (int) (mDirtyRect.right + mMaxWidth), + (int) (mDirtyRect.bottom + mMaxWidth)); + + return true; + } + + public void sendDragEventToReact() { + if (callback != null && dragged) { + callback.onDragged(); + } + } + + // all touch events during the drawing + @Override + protected void onDraw(Canvas canvas) { + if (mSignatureBitmap != null) { + canvas.drawBitmap(mSignatureBitmap, 0, 0, mPaint); + } + } + + + /** + * Called when replaying history to ensure the dirty region includes all + * mPoints. + * + * @param historicalX the previous x coordinate. + * @param historicalY the previous y coordinate. + */ + private void expandDirtyRect(float historicalX, float historicalY) { + if (historicalX < mDirtyRect.left) { + mDirtyRect.left = historicalX; + } else if (historicalX > mDirtyRect.right) { + mDirtyRect.right = historicalX; + } + if (historicalY < mDirtyRect.top) { + mDirtyRect.top = historicalY; + } else if (historicalY > mDirtyRect.bottom) { + mDirtyRect.bottom = historicalY; + } + } + + /** + * Resets the dirty region when the motion event occurs. + * + * @param eventX the event x coordinate. + * @param eventY the event y coordinate. + */ + private void resetDirtyRect(float eventX, float eventY) { + + // The mLastTouchX and mLastTouchY were set when the ACTION_DOWN motion event occurred. + mDirtyRect.left = Math.min(mLastTouchX, eventX); + mDirtyRect.right = Math.max(mLastTouchX, eventX); + mDirtyRect.top = Math.min(mLastTouchY, eventY); + mDirtyRect.bottom = Math.max(mLastTouchY, eventY); + } + + + private void setIsEmpty(boolean newValue) { + mIsEmpty = newValue; + if (mOnSignedListener != null) { + if (mIsEmpty) { + mOnSignedListener.onClear(); + } else { + mOnSignedListener.onSigned(); + } + } + } + + public void clear() { + dragged = false; + mPoints = new ArrayList(); + mLastVelocity = 0; + mLastWidth = (mMinWidth + mMaxWidth) / 2; + mPath.reset(); + + if (mSignatureBitmap != null) { + mSignatureBitmap = null; + ensureSignatureBitmap(); + } + + setIsEmpty(true); + + invalidate(); + } + + private int convertDpToPx(float dp){ + return Math.round(dp*(getResources().getDisplayMetrics().xdpi/ DisplayMetrics.DENSITY_DEFAULT)); + } + + public interface OnSignedListener { + public void onSigned(); + + public void onClear(); + } +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureViewManager.java b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureViewManager.java new file mode 100644 index 0000000000..be3774adee --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/android/src/main/java/com/rssignaturecapture/RSSignatureCaptureViewManager.java @@ -0,0 +1,164 @@ +package com.rssignaturecapture; + +import android.util.Log; +import android.graphics.Color; + +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.rssignaturecapture.RSSignatureCaptureContextModule; +import java.io.IOException; + +import java.util.Map; + +import javax.annotation.Nullable; + +public class RSSignatureCaptureViewManager extends ViewGroupManager { + + public static final String PROPS_SAVE_IMAGE_FILE="saveImageFileInExtStorage"; + public static final String PROPS_VIEW_MODE = "viewMode"; + public static final String PROPS_SHOW_NATIVE_BUTTONS="showNativeButtons"; + public static final String PROPS_MAX_SIZE="maxSize"; + public static final String PROPS_MIN_STROKE_WIDTH="minStrokeWidth"; + public static final String PROPS_MAX_STROKE_WIDTH="maxStrokeWidth"; + public static final String PROPS_STROKE_COLOR="strokeColor"; + public static final String PROPS_BACKGROUND_COLOR="backgroundColor"; + + public static final int COMMAND_SAVE_IMAGE = 1; + public static final int COMMAND_RESET_IMAGE = 2; + + private RSSignatureCaptureContextModule mContextModule; + + public RSSignatureCaptureViewManager(ReactApplicationContext reactContext) { + mContextModule = new RSSignatureCaptureContextModule(reactContext); + } + + @Override + public String getName() { + return "RSSignatureView"; + } + + @ReactProp(name = PROPS_SAVE_IMAGE_FILE) + public void setSaveImageFileInExtStorage(RSSignatureCaptureMainView view, @Nullable Boolean saveFile) { + Log.d("setFileInExtStorage:", "" + saveFile); + if(view!=null){ + view.setSaveFileInExtStorage(saveFile); + } + } + + @ReactProp(name = PROPS_VIEW_MODE) + public void setViewMode(RSSignatureCaptureMainView view, @Nullable String viewMode) { + Log.d("setViewMode:", "" + viewMode); + if(view!=null){ + view.setViewMode(viewMode); + } + } + + + @ReactProp(name = PROPS_SHOW_NATIVE_BUTTONS) + public void setPropsShowNativeButtons(RSSignatureCaptureMainView view, @Nullable Boolean showNativeButtons) { + Log.d("showNativeButtons:", "" + showNativeButtons); + if(view!=null){ + view.setShowNativeButtons(showNativeButtons); + } + } + + + @ReactProp(name = PROPS_MAX_SIZE) + public void setPropsWidth(RSSignatureCaptureMainView view, @Nullable Integer maxSize) { + Log.d("maxSize:", ""+maxSize); + if(view!=null){ + view.setMaxSize(maxSize); + } + } + + @ReactProp(name = PROPS_MIN_STROKE_WIDTH) + public void setPropsMinStrokeWidth(RSSignatureCaptureMainView view, @Nullable int minStrokeWidth) { + Log.d("minStrokeWidth:", ""+minStrokeWidth); + if(view!=null){ + view.getSignatureView().setMinStrokeWidth(minStrokeWidth); + } + } + + @ReactProp(name = PROPS_MAX_STROKE_WIDTH) + public void setPropsMaxStrokeWidth(RSSignatureCaptureMainView view, @Nullable int maxStrokeWidth) { + Log.d("maxStrokeWidth:", ""+maxStrokeWidth); + if(view!=null){ + view.getSignatureView().setMaxStrokeWidth(maxStrokeWidth); + } + } + + @ReactProp(name = PROPS_STROKE_COLOR) + public void setPropsStrokeColor(RSSignatureCaptureMainView view, @Nullable String color) { + Log.d("strokeColor:", ""+color); + if(view!=null){ + view.getSignatureView().setStrokeColor(Color.parseColor(color)); + } + } + + @ReactProp(name = PROPS_BACKGROUND_COLOR) + public void setPropsBackgroundColor(RSSignatureCaptureMainView view, @Nullable String color) { + int parsed; + if (color.equalsIgnoreCase("transparent")) { + parsed = Color.TRANSPARENT; + } else { + parsed = Color.parseColor(color); + } + + Log.d("backgroundColor:", ""+color); + if(view!=null){ + view.getSignatureView().setBackgroundColor(parsed); + } + } + + @Override + public RSSignatureCaptureMainView createViewInstance(ThemedReactContext context) { + Log.d("React"," View manager createViewInstance:"); + return new RSSignatureCaptureMainView(context, mContextModule.getActivity()); + } + + @Override + public Map getCommandsMap() { + Log.d("React"," View manager getCommandsMap:"); + return MapBuilder.of( + "saveImage", + COMMAND_SAVE_IMAGE, + "resetImage", + COMMAND_RESET_IMAGE); + } + + @Override + public void receiveCommand( + RSSignatureCaptureMainView view, + int commandType, + @Nullable ReadableArray args) { + Assertions.assertNotNull(view); + Assertions.assertNotNull(args); + switch (commandType) { + case COMMAND_SAVE_IMAGE: { + try { + view.saveImage(); + } catch (IOException e) { + e.printStackTrace(); + } + return; + } + case COMMAND_RESET_IMAGE: { + view.reset(); + return; + } + + default: + throw new IllegalArgumentException(String.format( + "Unsupported command %d received by %s.", + commandType, + getClass().getSimpleName())); + } + } + + +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureView.m b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureView.m new file mode 100644 index 0000000000..1727c3ee6c --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureView.m @@ -0,0 +1,250 @@ +#import "RSSignatureView.h" +#import +#import +#import +#import "PPSSignatureView.h" +#import "RSSignatureViewManager.h" + +#define DEGREES_TO_RADIANS(x) (M_PI * (x) / 180.0) + +@implementation RSSignatureView { + CAShapeLayer *_border; + BOOL _loaded; + EAGLContext *_context; + UIButton *saveButton; + UIButton *clearButton; + UILabel *titleLabel; + BOOL _rotateClockwise; + BOOL _square; + BOOL _showBorder; + BOOL _showNativeButtons; + BOOL _showTitleLabel; + NSString *_viewMode; + UIColor *_backgroundColor; + UIColor *_strokeColor; +} + +@synthesize sign; +@synthesize manager; + +- (instancetype)init +{ + _showBorder = YES; + _showNativeButtons = YES; + _showTitleLabel = YES; + _backgroundColor = UIColor.whiteColor; + _strokeColor = UIColor.blackColor; + if ((self = [super init])) { + _border = [CAShapeLayer layer]; + _border.strokeColor = [UIColor blackColor].CGColor; + _border.fillColor = nil; + _border.lineDashPattern = @[@4, @2]; + + [self.layer addSublayer:_border]; + } + + return self; +} + +- (void) didRotate:(NSNotification *)notification { + int ori=1; + UIDeviceOrientation currOri = [[UIDevice currentDevice] orientation]; + if ((currOri == UIDeviceOrientationLandscapeLeft) || (currOri == UIDeviceOrientationLandscapeRight)) { + ori=0; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + if (!_loaded) { + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) + name:UIDeviceOrientationDidChangeNotification object:nil]; + + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + + CGSize screen = self.bounds.size; + + sign = [[PPSSignatureView alloc] + initWithFrame: CGRectMake(0, 0, screen.width, screen.height) + context: _context]; + sign.manager = manager; + sign.backgroundColor = _backgroundColor; + sign.strokeColor = _strokeColor; + + [self addSubview:sign]; + + if ( [_viewMode isEqual: @"portrait"] || UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) { + + if (_showTitleLabel) { + titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 24)]; + [titleLabel setCenter:CGPointMake(self.bounds.size.width/2, self.bounds.size.height - 120)]; + + [titleLabel setText:@"x_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _"]; + [titleLabel setLineBreakMode:NSLineBreakByClipping]; + [titleLabel setTextAlignment: NSTextAlignmentCenter]; + [titleLabel setTextColor:[UIColor colorWithRed:200/255.f green:200/255.f blue:200/255.f alpha:1.f]]; + //[titleLabel setBackgroundColor:[UIColor greenColor]]; + [sign addSubview:titleLabel]; + } + + if (_showNativeButtons) { + //Save button + saveButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [saveButton setLineBreakMode:NSLineBreakByClipping]; + [saveButton addTarget:self action:@selector(onSaveButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + [saveButton setTitle:@"Save" forState:UIControlStateNormal]; + + CGSize buttonSize = CGSizeMake(80, 55.0); + + saveButton.frame = CGRectMake(sign.bounds.size.width - buttonSize.width, + 0, buttonSize.width, buttonSize.height); + [saveButton setBackgroundColor:[UIColor colorWithRed:250/255.f green:250/255.f blue:250/255.f alpha:1.f]]; + [sign addSubview:saveButton]; + + + //Clear button + clearButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [clearButton setLineBreakMode:NSLineBreakByClipping]; + [clearButton addTarget:self action:@selector(onClearButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + [clearButton setTitle:@"Reset" forState:UIControlStateNormal]; + + clearButton.frame = CGRectMake(0, 0, buttonSize.width, buttonSize.height); + [clearButton setBackgroundColor:[UIColor colorWithRed:250/255.f green:250/255.f blue:250/255.f alpha:1.f]]; + [sign addSubview:clearButton]; + } + } + else { + + if (_showTitleLabel) { + titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.height - 80, 24)]; + [titleLabel setCenter:CGPointMake(40, self.bounds.size.height/2)]; + [titleLabel setTransform:CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(90))]; + [titleLabel setText:@"x_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _"]; + [titleLabel setLineBreakMode:NSLineBreakByClipping]; + [titleLabel setTextAlignment: NSTextAlignmentLeft]; + [titleLabel setTextColor:[UIColor colorWithRed:200/255.f green:200/255.f blue:200/255.f alpha:1.f]]; + //[titleLabel setBackgroundColor:[UIColor greenColor]]; + [sign addSubview:titleLabel]; + } + + if (_showNativeButtons) { + //Save button + saveButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [saveButton setTransform:CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(90))]; + [saveButton setLineBreakMode:NSLineBreakByClipping]; + [saveButton addTarget:self action:@selector(onSaveButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + [saveButton setTitle:@"Save" forState:UIControlStateNormal]; + + CGSize buttonSize = CGSizeMake(55, 80.0); //Width/Height is swapped + + saveButton.frame = CGRectMake(sign.bounds.size.width - buttonSize.width, sign.bounds.size.height - buttonSize.height, buttonSize.width, buttonSize.height); + [saveButton setBackgroundColor:[UIColor colorWithRed:250/255.f green:250/255.f blue:250/255.f alpha:1.f]]; + [sign addSubview:saveButton]; + + //Clear button + clearButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [clearButton setTransform:CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(90))]; + [clearButton setLineBreakMode:NSLineBreakByClipping]; + [clearButton addTarget:self action:@selector(onClearButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + [clearButton setTitle:@"Reset" forState:UIControlStateNormal]; + + clearButton.frame = CGRectMake(sign.bounds.size.width - buttonSize.width, 0, buttonSize.width, buttonSize.height); + [clearButton setBackgroundColor:[UIColor colorWithRed:250/255.f green:250/255.f blue:250/255.f alpha:1.f]]; + [sign addSubview:clearButton]; + } + } + + } + _loaded = true; + _border.path = _showBorder ? [UIBezierPath bezierPathWithRect:self.bounds].CGPath : nil; + _border.frame = self.bounds; +} + +- (void)setRotateClockwise:(BOOL)rotateClockwise { + _rotateClockwise = rotateClockwise; +} + +- (void)setSquare:(BOOL)square { + _square = square; +} + +- (void)setShowBorder:(BOOL)showBorder { + _showBorder = showBorder; +} + +- (void)setShowNativeButtons:(BOOL)showNativeButtons { + _showNativeButtons = showNativeButtons; +} + +- (void)setShowTitleLabel:(BOOL)showTitleLabel { + _showTitleLabel = showTitleLabel; +} + +- (void)setBackgroundColor:(UIColor*)backgroundColor { + _backgroundColor = backgroundColor; +} + +- (void)setStrokeColor:(UIColor*)strokeColor { + _strokeColor = strokeColor; +} + +- (void)setViewMode:(NSString *)viewMode { + _viewMode = viewMode; +} + +-(void) onSaveButtonPressed { + [self saveImage]; +} + +-(void) saveImage { + saveButton.hidden = YES; + clearButton.hidden = YES; + UIImage *signImage = [self.sign signatureImage: _rotateClockwise withSquare:_square]; + + saveButton.hidden = NO; + clearButton.hidden = NO; + + NSError *error; + + NSString * timestamp = [NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970]]; + timestamp = [timestamp stringByAppendingString:@".png"]; + + NSString *tempPath = [NSTemporaryDirectory() stringByAppendingFormat:@"signature"]; + tempPath = [tempPath stringByAppendingString:timestamp]; + + //remove if file already exists + if ([[NSFileManager defaultManager] fileExistsAtPath:tempPath]) { + [[NSFileManager defaultManager] removeItemAtPath:tempPath error:&error]; + if (error) { + NSLog(@"Error: %@", error.debugDescription); + } + } + + // Convert UIImage object into NSData (a wrapper for a stream of bytes) formatted according to PNG spec + NSData *imageData = UIImagePNGRepresentation(signImage); + BOOL isSuccess = [imageData writeToFile:tempPath atomically:YES]; + if (isSuccess) { + NSFileManager *man = [NSFileManager defaultManager]; + NSDictionary *attrs = [man attributesOfItemAtPath:tempPath error: NULL]; + //UInt32 result = [attrs fileSize]; + + NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0]; + [self.manager publishSaveImageEvent: tempPath withEncoded:base64Encoded]; + } +} + +-(void) onClearButtonPressed { + [self erase]; +} + +-(void) erase { + [self.sign erase]; +} + +@end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureViewManager.m b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureViewManager.m new file mode 100644 index 0000000000..fc2abd1394 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-signature-capture/overrides/ios/RSSignatureViewManager.m @@ -0,0 +1,64 @@ +#import "RSSignatureViewManager.h" +#import +#import +#import + +@implementation RSSignatureViewManager + +@synthesize bridge = _bridge; +@synthesize signView; + +RCT_EXPORT_MODULE() + +RCT_EXPORT_VIEW_PROPERTY(rotateClockwise, BOOL) +RCT_EXPORT_VIEW_PROPERTY(square, BOOL) +RCT_EXPORT_VIEW_PROPERTY(showBorder, BOOL) +RCT_EXPORT_VIEW_PROPERTY(showNativeButtons, BOOL) +RCT_EXPORT_VIEW_PROPERTY(showTitleLabel, BOOL) +RCT_EXPORT_VIEW_PROPERTY(viewMode, NSString *) +RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(strokeColor, UIColor) + + +-(dispatch_queue_t) methodQueue +{ + return dispatch_get_main_queue(); +} + +-(UIView *) view +{ + self.signView = [[RSSignatureView alloc] init]; + self.signView.manager = self; + return signView; +} + +// Both of these methods needs to be called from the main thread so the +// UI can clear out the signature. +RCT_EXPORT_METHOD(saveImage:(nonnull NSNumber *)reactTag) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.signView saveImage]; + }); +} + +RCT_EXPORT_METHOD(resetImage:(nonnull NSNumber *)reactTag) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.signView erase]; + }); +} + +-(void) publishSaveImageEvent:(NSString *) aTempPath withEncoded: (NSString *) aEncoded { + [self.bridge.eventDispatcher + sendDeviceEventWithName:@"onSaveEvent" + body:@{ + @"pathName": aTempPath, + @"encoded": aEncoded + }]; +} + +-(void) publishDraggedEvent { + [self.bridge.eventDispatcher + sendDeviceEventWithName:@"onDragEvent" + body:@{@"dragged": @YES}]; +} + +@end \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/build.gradle new file mode 100644 index 0000000000..aedd9e4aef --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/build.gradle @@ -0,0 +1,25 @@ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + lintOptions { + warning 'InvalidPackage' + } +} + +dependencies { + compile 'com.facebook.react:react-native:+' +} + \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/src/main/java/com/reactlibrary/RNSimpleCompassPackage.java b/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/src/main/java/com/reactlibrary/RNSimpleCompassPackage.java new file mode 100644 index 0000000000..a755ffaa16 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-compass/overrides/android/src/main/java/com/reactlibrary/RNSimpleCompassPackage.java @@ -0,0 +1,23 @@ +// REMOVED depracated method +package com.reactlibrary; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; +import com.facebook.react.bridge.JavaScriptModule; +public class RNSimpleCompassPackage implements ReactPackage { + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + return Arrays.asList(new RNSimpleCompassModule(reactContext)); + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/BlurBuilder.java b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/BlurBuilder.java new file mode 100644 index 0000000000..cddb6f5ada --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/BlurBuilder.java @@ -0,0 +1,47 @@ +package com.como.RNTShadowView; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.util.Log; + +public class BlurBuilder { + + public static Bitmap blur(Context context, Bitmap image, float blurRadius) { + // Override + // blurRadius can't be 0 since Math.sqrt is failing + blurRadius = blurRadius == 0 ? 1 : blurRadius; + // End of Override + float scale = (1.3f / (float)Math.sqrt(blurRadius * Resources.getSystem().getDisplayMetrics().density)) ; + blurRadius = blurRadius * 2; + blurRadius = Math.max(8, Math.min(25, blurRadius)); + return blur(context, image, blurRadius, scale); + } + + public static Bitmap blur(Context context, Bitmap image, float blurRadius, float scale) { + int width = Math.round(image.getWidth() * scale); + int height = Math.round(image.getHeight() * scale); + + Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false); + Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); + + RenderScript rs = RenderScript.create(context); + + ScriptIntrinsicBlur intrinsicBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); + Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); + + intrinsicBlur.setRadius(blurRadius); + intrinsicBlur.setInput(tmpIn); + intrinsicBlur.forEach(tmpOut); + tmpOut.copyTo(outputBitmap); + + return outputBitmap; + } +} + + diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/RNTShadowViewManager.java b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/RNTShadowViewManager.java new file mode 100644 index 0000000000..cc74cf5c0e --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/RNTShadowViewManager.java @@ -0,0 +1,215 @@ +package com.como.RNTShadowView; + +import android.graphics.Color; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.Spacing; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewProps; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import com.como.RNTShadowView.ShadowView; +import com.facebook.react.uimanager.annotations.ReactPropGroup; +import com.facebook.react.views.view.ReactViewGroup; +import com.facebook.yoga.YogaConstants; + +public class RNTShadowViewManager extends ViewGroupManager { + public static final String REACT_CLASS = "RNTShadowView"; + private static final int[] SPACING_TYPES = { + Spacing.ALL, + Spacing.LEFT, + Spacing.RIGHT, + Spacing.TOP, + Spacing.BOTTOM, + Spacing.START, + Spacing.END, + }; + + @ReactPropGroup( + names = { + ViewProps.BORDER_RADIUS, + ViewProps.BORDER_TOP_LEFT_RADIUS, + ViewProps.BORDER_TOP_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_LEFT_RADIUS, + ViewProps.BORDER_TOP_START_RADIUS, + ViewProps.BORDER_TOP_END_RADIUS, + ViewProps.BORDER_BOTTOM_START_RADIUS, + ViewProps.BORDER_BOTTOM_END_RADIUS, + }, + defaultFloat = YogaConstants.UNDEFINED) + public void setBorderRadius(ReactViewGroup view, int index, float borderRadius) { + if (!YogaConstants.isUndefined(borderRadius) && borderRadius < 0) { + borderRadius = YogaConstants.UNDEFINED; + } + + if (!YogaConstants.isUndefined(borderRadius)) { + borderRadius = PixelUtil.toPixelFromDIP(borderRadius); + } + + if (index == 0) { + view.setBorderRadius(borderRadius); + } else { + view.setBorderRadius(borderRadius, index - 1); + } + } + + @ReactProp(name = "borderStyle") + public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) { + view.setBorderStyle(borderStyle); + } + + @ReactPropGroup( + names = { + ViewProps.BORDER_WIDTH, + ViewProps.BORDER_LEFT_WIDTH, + ViewProps.BORDER_RIGHT_WIDTH, + ViewProps.BORDER_TOP_WIDTH, + ViewProps.BORDER_BOTTOM_WIDTH, + ViewProps.BORDER_START_WIDTH, + ViewProps.BORDER_END_WIDTH, + }, + defaultFloat = YogaConstants.UNDEFINED) + public void setBorderWidth(ReactViewGroup view, int index, float width) { + if (!YogaConstants.isUndefined(width) && width < 0) { + width = YogaConstants.UNDEFINED; + } + + if (!YogaConstants.isUndefined(width)) { + width = PixelUtil.toPixelFromDIP(width); + } + + view.setBorderWidth(SPACING_TYPES[index], width); + } + + @ReactPropGroup( + names = { + ViewProps.BORDER_COLOR, + ViewProps.BORDER_LEFT_COLOR, + ViewProps.BORDER_RIGHT_COLOR, + ViewProps.BORDER_TOP_COLOR, + ViewProps.BORDER_BOTTOM_COLOR, + ViewProps.BORDER_START_COLOR, + ViewProps.BORDER_END_COLOR + }, + customType = "Color") + public void setBorderColor(ReactViewGroup view, int index, Integer color) { + float rgbComponent = + color == null ? YogaConstants.UNDEFINED : (float) ((int) color & 0x00FFFFFF); + float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int) color >>> 24); + view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent); + } + + @ReactProp(name = "shadowBorderRadius", defaultDouble = 0) + public void shadowBorderRadius(final ShadowView shadowView, @Nullable double borderRadius) { + if (shadowView != null) { + shadowView.setShadowBorderRadius(borderRadius); + } + } +// +// @ReactProp(name = "borderColor") +// public void setBorderColor(final ShadowView shadowView, @Nullable String borderColor) { +// if (shadowView != null) { +// shadowView.setBorderColor(parseColor(borderColor)); +// } +// } +// +// @ReactProp(name = "borderWidth") +// public void setBorderWidth(final ShadowView shadowView, @Nullable double borderWidth) { +// if (shadowView != null) { +// shadowView.setBorderWidth(borderWidth); +// } +// } + + + + @ReactProp(name = "backgroundColor") + public void setBackgroundColor(final ShadowView shadowView, @Nullable String backgroundColor) { + if (shadowView != null) { + shadowView.setBackgroundColor(parseColor(backgroundColor)); + } + } + + @ReactProp(name = "shadowColor") + public void setShadowColor(final ShadowView shadowView, @Nullable String shadowColor) { + if (shadowView != null) { + shadowView.setShadowColor(parseColor(shadowColor)); + } + } + + @ReactProp(name = "shadowOffsetX", defaultDouble = 0) + public void setShadowOffsetX(final ShadowView shadowView, @Nullable double shadowOffsetX) { + if (shadowView != null) { + shadowView.setShadowOffsetX(shadowOffsetX); + } + } + + @ReactProp(name = "shadowOffsetY", defaultDouble = 0) + public void setShadowOffsetY(final ShadowView shadowView, @Nullable double shadowOffsetY) { + if (shadowView != null) { + shadowView.setShadowOffsetY(shadowOffsetY); + } + } + + @ReactProp(name = "shadowOpacity", defaultDouble = 1) + public void setShadowOpacity(final ShadowView shadowView, @Nullable double shadowOpacity) { + if (shadowView != null) { + shadowView.setShadowOpacity(shadowOpacity); + } + } + + @ReactProp(name = "shadowRadius") + public void setShadowRadius(final ShadowView shadowView, double shadowRadius) { + if (shadowView != null) { + shadowView.setShadowRadius(shadowRadius); + } + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + public ShadowView createViewInstance(ThemedReactContext context) { + return new ShadowView(context); + } + + private int parseColor(String colorString) { + if (colorString == null) { + return Color.TRANSPARENT; + } + Pattern pattern = Pattern.compile("\\((\\d+),(\\d+),(\\d+)(,([\\d|\\.]+)|.*?)"); + + String colorStringNoWS = colorString.replace(" ", ""); + Matcher m = pattern.matcher(colorStringNoWS); + + if (m.find()) { + int red = Integer.parseInt(m.group(1)); + int green = Integer.parseInt(m.group(2)); + int blue = Integer.parseInt(m.group(3)); + int alpha = 255; + if (m.groupCount() == 5) { + Pattern alphaPattern = Pattern.compile("[\\d|\\.]+"); + Matcher alphaMatch = alphaPattern.matcher(m.group(4)); + if (alphaMatch.find()) { + alpha = (int)(Double.parseDouble(alphaMatch.group(0)) * 255); + } + } + return Color.argb(alpha, red, green, blue); + } else { + try { + return Color.parseColor(colorString); + } + catch(Exception ex) { + return Color.BLACK; + } + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/ShadowView.java b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/ShadowView.java new file mode 100644 index 0000000000..8865ea5828 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/android/src/main/java/com/reactlibrary/ShadowView.java @@ -0,0 +1,158 @@ +package com.como.RNTShadowView; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.facebook.react.views.view.ReactViewGroup; + +public class ShadowView extends ReactViewGroup { + int shadowOffsetX = 0; + int shadowOffsetY = (int)(-2 * Resources.getSystem().getDisplayMetrics().density); + int shadowRadius = 0; + int borderRadius = 0; + int shadowColor; + int shadowColorToDraw; + int borderShadowColor; + int shadowOpacity; + int margin; + double borderWidth; + + Paint viewPaint = new Paint(); + Paint borderPaint = new Paint(); + + Bitmap shadowBitmap = null; + + public ShadowView(Context context) { + super(context); + init(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + if (changed) { + // Override to prevent crashing if size is 0 + if (getWidth() == 0 || getHeight() == 0) { + return; + } + shadowBitmap = createShadowForView(); + invalidate(); + } + } + + @Override + public void setBackgroundColor(int color) { + super.setBackgroundColor(Color.TRANSPARENT); + viewPaint.setColor(color); + createShadowColor(); + invalidate(); + } + + private void init() { + setLayerType(View.LAYER_TYPE_NONE, viewPaint); + viewPaint.setAntiAlias(true); + borderPaint.setAntiAlias(true); + borderPaint.setStyle(Paint.Style.STROKE); + borderPaint.setColor(Color.BLACK); + shadowColor = Color.BLACK; + shadowColorToDraw = Color.BLACK; + + createShadowColor(); + invalidate(); + } + + public void setShadowBorderRadius(double borderRadius) { + this.borderRadius=(int) (borderRadius * Resources.getSystem().getDisplayMetrics().density); + invalidate(); + } + + public void setShadowOffsetX(double shadowOffsetX) { + this.shadowOffsetX = (int)((shadowOffsetX * Resources.getSystem().getDisplayMetrics().density)); + invalidate(); + } + + public void setShadowOffsetY(double shadowOffsetY) { + this.shadowOffsetY = (int)((shadowOffsetY * Resources.getSystem().getDisplayMetrics().density)); + invalidate(); + } + + public void setShadowColor(int shadowColor) { + this.shadowColor = shadowColor; + createShadowColor(); + invalidate(); + } + + public void setShadowOpacity(double shadowOpacity) { + shadowOpacity = Math.min(Math.max(0, shadowOpacity), 1); + this.shadowOpacity = (int)(shadowOpacity * 255); + this.createShadowColor(); + invalidate(); + } + + public void setShadowRadius(double shadowRadius) { + shadowRadius = Math.max(0.2, shadowRadius); + this.shadowRadius = (int)shadowRadius; + this.margin = (int)(this.shadowRadius * 6.2); + invalidate(); + } + + public void setBorderColor(int borderColor) { + borderPaint.setColor(borderColor); + createShadowColor(); + invalidate(); + } + +// public void setBorderWidth(double borderWidth) { +//// this.borderWidth = (borderWidth * Resources.getSystem().getDisplayMetrics().density * 1.1); +// this.borderWidth = 3; +// invalidate(); +// } + + private void createShadowColor() { + int red = Color.red(shadowColor); + int green = Color.green(shadowColor); + int blue = Color.blue(shadowColor); + int shadowColorAlpha = Color.alpha(shadowColor); + int borderColorAlpha = Color.alpha(borderPaint.getColor()); + int shadowAlpha = (int)((double)shadowOpacity * ((double)shadowColorAlpha/255.0) ); + int borderShadowAlpha = (int)((double)shadowOpacity * ((double)borderColorAlpha/255.0)); + shadowColorToDraw = Color.argb(shadowAlpha, red, green, blue); + borderShadowColor = Color.argb(borderShadowAlpha, red, green, blue); + } + + @Override + protected void onDraw(Canvas canvas) { + // Override to prevent crashing if size is 0 + if (getWidth() == 0 || getHeight() == 0) { + return; + } + + Rect imageRect = new Rect(0, 0, shadowBitmap.getWidth(), shadowBitmap.getHeight()); + Rect targetRect = new Rect(shadowOffsetX - margin, shadowOffsetY - margin, getWidth() + margin + shadowOffsetX, getHeight() + margin + shadowOffsetY); + canvas.drawBitmap(shadowBitmap, imageRect, targetRect, viewPaint); + + Object contentRect = new RectF(0, 0, getWidth(), getHeight()); + canvas.drawRoundRect((RectF)contentRect, borderRadius, borderRadius, viewPaint); + + } + + public Bitmap createShadowForView() { + Bitmap bitmap = Bitmap.createBitmap(getWidth() + margin * 2, getHeight()+ margin * 2, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + Paint shadowPaint = new Paint(); + shadowPaint.setAntiAlias(true); + shadowPaint.setColor(shadowColorToDraw); + canvas.drawRoundRect( new RectF(margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin), borderRadius, borderRadius, shadowPaint); + return com.como.RNTShadowView.BlurBuilder.blur(getContext(), bitmap, shadowRadius); + } +} diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/index.js b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/index.js new file mode 100644 index 0000000000..622c5aebbb --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/index.js @@ -0,0 +1,7 @@ +import ShadowView from './src/ShadowView'; +import ShadowViewTouchable from './src/ShadowViewTouchable'; + +export { + ShadowView, + ShadowViewTouchable +}; diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowView.js b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowView.js new file mode 100644 index 0000000000..1cf627c374 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowView.js @@ -0,0 +1,79 @@ +// OVERRIDEN +import React, { Component } from 'react'; +import { requireNativeComponent, Platform, View } from 'react-native'; + +export const RNTShadowView = requireNativeComponent('RNTShadowView', ShadowView); + +class ShadowView extends Component { + constructor(props) { + super(props); + } + + _getBackgroundColor(backgroundColor) { + if (backgroundColor) { + if (backgroundColor === 'transparent' || backgroundColor.replace(' ', '') === 'rgba(0,0,0,0)') { + return 'rgba(0,0,0,0.001)' + } + } + + return backgroundColor !== undefined && typeof backgroundColor === 'string' ? backgroundColor : undefined; + }; + + render() { + if (Platform.OS === 'ios') { + return ( + + ); + } + const { style } = this.props || {}; + let flattenedStyle = {}; + if (Array.isArray(style)) { + style.map((item) => { + item && Object.keys(item) && Object.keys(item).map(key => flattenedStyle[key] = item[key]); + }); + } else { + flattenedStyle = style || {}; + } + + delete flattenedStyle.elevation; + + const { + shadowColor, + shadowOffset, + shadowOpacity, + shadowRadius, + borderRadius, + backgroundColor, + borderWidth, + borderColor, + } = flattenedStyle; + + if (!shadowRadius || shadowOpacity === 0) { + return (); + } + + const { width: shadowOffsetX, height: shadowOffsetY } = shadowOffset || {}; + + return ( + + {this.props.children} + + ); + } +} + +export default ShadowView; diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.js b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.js new file mode 100644 index 0000000000..bc191f9800 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.js @@ -0,0 +1,233 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + + import React from 'react'; + import * as Animatable from 'react-native-animatable'; + import { Easing, TouchableWithoutFeedback, Animated } from 'react-native'; + import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle'; + import {PressabilityDebugView} from 'react-native/Libraries/Pressability/PressabilityDebug'; + import Pressability, { type PressabilityConfig } from 'react-native/Libraries/Pressability/Pressability'; + + import { RNTShadowView } from './ShadowView'; + + type TVProps = $ReadOnly<{| + hasTVPreferredFocus?: ?boolean, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, + |}>; + + type Props = $ReadOnly<{| + ...React.ElementConfig, + ...TVProps, + + activeOpacity?: ?number, + style?: ?ViewStyleProp, + + hostRef: React.Ref, + |}>; + + type State = $ReadOnly<{| + anim: Animated.Value, + pressability: Pressability, + |}>; + + + class ShadowViewTouchable extends React.Component { + + state: State = { + anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), + pressability: new Pressability(this._createPressabilityConfig()), + }; + + ref = React.createRef(); + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + minPressDuration: 0, + pressRectOffset: this.props.pressRetentionOffset, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, + onPressIn: event => { + this._opacityActive( + event.dispatchConfig.registrationName === 'onResponderGrant' + ? 0 + : 150, + ); + if (this.props.onPressIn != null) { + this.props.onPressIn(event); + } + }, + onPressOut: event => { + this._opacityInactive(250); + if (this.props.onPressOut != null) { + this.props.onPressOut(event); + } + }, + }; + } + + /** + * Animate the touchable to a new opacity. + */ + _setOpacityTo(toValue: number, duration: number): void { + Animated.timing(this.state.anim, { + toValue, + duration, + easing: Easing.inOut(Easing.quad), + useNativeDriver: false, + }).start(); + } + + _opacityActive(duration: number): void { + // this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration); + this.ref.current.setNativeProps({ + style: { + opacity: 0.2 + } + }) + } + + _opacityInactive(duration: number): void { + // this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration); + this.ref.current.setNativeProps({ + style: { + opacity: this._getChildStyleOpacityWithDefault() + } + }) + } + + _getChildStyleOpacityWithDefault(): number { + const opacity = flattenStyle(this.props.style)?.opacity; + return typeof opacity === 'number' ? opacity : 1; + } + + _getBackgroundColor(backgroundColor): string { + if (backgroundColor) { + if (backgroundColor === 'transparent' || backgroundColor.replace(' ', '') === 'rgba(0,0,0,0)') { + return 'rgba(0,0,0,0.001)' + } + } + + return backgroundColor !== undefined && typeof backgroundColor === 'string' ? backgroundColor : undefined; + }; + + render() { + const { style } = this.props || {}; + const styles = {...style}; + let flattenedStyle = {}; + if (Array.isArray(styles)) { + styles.map((item) => { + item && Object.keys(item) && Object.keys(item).map(key => flattenedStyle[key] = item[key]); + }); + } else { + flattenedStyle = styles || {}; + } + + delete flattenedStyle.elevation; + + const { + shadowColor, + shadowOffset, + shadowOpacity, + shadowRadius, + borderRadius, + backgroundColor, + borderWidth, + borderColor, + } = flattenedStyle; + + const { + onBlur, + onFocus, + ...eventHandlersWithoutBlurAndFocus + } = this.state.pressability.getEventHandlers(); + + const { width: shadowOffsetX, height: shadowOffsetY } = shadowOffset || {}; + + return ( + + {this.props.children} + {__DEV__ ? ( + + ) : null} + + ); + } + + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + if (this.props.disabled !== prevProps.disabled) { + this._opacityInactive(250); + } + } + + componentWillUnmount(): void { + this.state.pressability.reset(); + } + } + + module.exports = ShadowViewTouchable; + \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.tv.native.js b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.tv.native.js new file mode 100644 index 0000000000..39171a013e --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-simple-shadow-view/overrides/src/ShadowViewTouchable.tv.native.js @@ -0,0 +1,261 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +import React from 'react'; +import * as Animatable from 'react-native-animatable'; +import { Easing, Platform, TouchableWithoutFeedback, Animated } from 'react-native'; +import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle'; +import TVTouchable from 'react-native/Libraries/Components/Touchable/TVTouchable'; +import {PressabilityDebugView} from 'react-native/Libraries/Pressability/PressabilityDebug'; +import Pressability, { type PressabilityConfig } from 'react-native/Libraries/Pressability/Pressability'; + +import ShadowView from './ShadowView'; + +const createReactClass = require('create-react-class'); + +const AnimatedComponent = Animatable.createAnimatableComponent(ShadowView); + +type TVProps = $ReadOnly<{| + hasTVPreferredFocus?: ?boolean, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, + |}>; + + type Props = $ReadOnly<{| + ...React.ElementConfig, + ...TVProps, + + activeOpacity?: ?number, + style?: ?ViewStyleProp, + + hostRef: React.Ref, + |}>; + + type State = $ReadOnly<{| + anim: Animated.Value, + pressability: Pressability, + |}>; + + +class ShadowViewTouchable extends React.Component { + _tvTouchable: ?TVTouchable; + + state: State = { + anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), + pressability: new Pressability(this._createPressabilityConfig()), + }; + + ref = React.createRef(); + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + minPressDuration: 0, + pressRectOffset: this.props.pressRetentionOffset, + onBlur: event => { + if (Platform.isTV) { + this._opacityInactive(250); + } + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (Platform.isTV) { + this._opacityActive(150); + } + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, + onPressIn: event => { + this._opacityActive( + event.dispatchConfig.registrationName === 'onResponderGrant' + ? 0 + : 150, + ); + if (this.props.onPressIn != null) { + this.props.onPressIn(event); + } + }, + onPressOut: event => { + this._opacityInactive(250); + if (this.props.onPressOut != null) { + this.props.onPressOut(event); + } + }, + }; + } + + /** + * Animate the touchable to a new opacity. + */ + _setOpacityTo(toValue: number, duration: number): void { + Animated.timing(this.state.anim, { + toValue, + duration, + easing: Easing.inOut(Easing.quad), + useNativeDriver: false, + }).start(); + } + + _opacityActive(duration: number): void { + // this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration); + this.ref.current.setNativeProps({ + style: { + opacity: 0.2 + } + }) + } + + _opacityInactive(duration: number): void { + // this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration); + this.ref.current.setNativeProps({ + style: { + opacity: 1 + } + }) + } + + _getChildStyleOpacityWithDefault(): number { + const opacity = flattenStyle(this.props.style)?.opacity; + return typeof opacity === 'number' ? opacity : 1; + } + + render() { + const { style } = this.props || {}; + let flattenedStyle = {}; + if (Array.isArray(style)) { + style.map((item) => { + item && Object.keys(item) && Object.keys(item).map(key => flattenedStyle[key] = item[key]); + }); + } else { + flattenedStyle = style || {}; + } + + delete flattenedStyle.elevation; + + const { + shadowColor, + shadowOffset, + shadowOpacity, + shadowRadius, + borderRadius, + backgroundColor, + borderWidth, + borderColor, + } = flattenedStyle; + + const { + onBlur, + onFocus, + ...eventHandlersWithoutBlurAndFocus + } = this.state.pressability.getEventHandlers(); + + const { width: shadowOffsetX, height: shadowOffsetY } = shadowOffset || {}; + + return ( + + {this.props.children} + {__DEV__ ? ( + + ) : null} + + ); + } + + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null) { + this.props.onPress(event); + } + }, + }); + } + } + + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + if (this.props.disabled !== prevProps.disabled) { + this._opacityInactive(250); + } + } + + componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } + this.state.pressability.reset(); + } +} + +module.exports = ShadowViewTouchable; diff --git a/packages/rnv/pluginTemplates/react-native-splash-screen/android/build.gradle b/packages/rnv/pluginTemplates/react-native-splash-screen/android/build.gradle new file mode 100644 index 0000000000..0f702a98fc --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-splash-screen/android/build.gradle @@ -0,0 +1,54 @@ +buildscript { + ext.safeExtGet = {prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback + } + if (project == rootProject) { + repositories { + google() + jcenter() + maven { + url "https://jitpack.io" + } + } + + dependencies { + //noinspection GradleDependency + classpath("com.android.tools.build:gradle:${safeExtGet('gradlePluginVersion', '3.4.1')}") + } + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 29) + //noinspection GradleDependency + buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') + + defaultConfig { + minSdkVersion safeExtGet('minSdkVersion', 16) + //noinspection OldTargetApi + targetSdkVersion safeExtGet('targetSdkVersion', 28) + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenLocal() + google() + jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + implementation "androidx.appcompat:appcompat:1.0.0" + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:${safeExtGet('reactnativeVersion', '+')}" +} diff --git a/packages/rnv/pluginTemplates/react-native-splash-screen/android/gradle.properties b/packages/rnv/pluginTemplates/react-native-splash-screen/android/gradle.properties new file mode 100644 index 0000000000..c63cc0f2a0 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-splash-screen/android/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true diff --git a/packages/rnv/pluginTemplates/react-native-svg/overrides/src/xml.tsx b/packages/rnv/pluginTemplates/react-native-svg/overrides/src/xml.tsx new file mode 100644 index 0000000000..0828675166 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-svg/overrides/src/xml.tsx @@ -0,0 +1,562 @@ +import React, { + Component, + ComponentType, + useEffect, + useMemo, + useState, +} from 'react'; +import Rect from './elements/Rect'; +import Circle from './elements/Circle'; +import Ellipse from './elements/Ellipse'; +import Polygon from './elements/Polygon'; +import Polyline from './elements/Polyline'; +import Line from './elements/Line'; +import Svg from './elements/Svg'; +import Path from './elements/Path'; +import G from './elements/G'; +import Text from './elements/Text'; +import TSpan from './elements/TSpan'; +import TextPath from './elements/TextPath'; +import Use from './elements/Use'; +import Image from './elements/Image'; +import Symbol from './elements/Symbol'; +import Defs from './elements/Defs'; +import LinearGradient from './elements/LinearGradient'; +import RadialGradient from './elements/RadialGradient'; +import Stop from './elements/Stop'; +import ClipPath from './elements/ClipPath'; +import Pattern from './elements/Pattern'; +import Mask from './elements/Mask'; +import Marker from './elements/Marker'; + +export const tags: { [tag: string]: ComponentType } = { + svg: Svg, + circle: Circle, + ellipse: Ellipse, + g: G, + text: Text, + tspan: TSpan, + textPath: TextPath, + path: Path, + polygon: Polygon, + polyline: Polyline, + line: Line, + rect: Rect, + use: Use, + image: Image, + symbol: Symbol, + defs: Defs, + linearGradient: LinearGradient, + radialGradient: RadialGradient, + stop: Stop, + clipPath: ClipPath, + pattern: Pattern, + mask: Mask, + marker: Marker, +}; + +function missingTag() { + return null; +} + +export interface AST { + tag: string; + style?: Styles; + styles?: string; + priority?: Map; + parent: AST | null; + children: (AST | string)[] | (JSX.Element | string)[]; + props: { + [prop: string]: Styles | string | undefined; + }; + Tag: ComponentType; +} + +export interface XmlAST extends AST { + children: (XmlAST | string)[]; + parent: XmlAST | null; +} + +export interface JsxAST extends AST { + children: (JSX.Element | string)[]; +} + +export type AdditionalProps = { + onError?: (error: Error) => void; + override?: Object; + headers?: { [key: string]: string } +}; + +type HeaderProps = { + headers?: { [key: string]: string } +} + +export type UriProps = { uri: string | null } & AdditionalProps & HeaderProps; +export type UriState = { xml: string | null }; + +export type XmlProps = { xml: string | null } & AdditionalProps; +export type XmlState = { ast: JsxAST | null }; + +export type AstProps = { ast: JsxAST | null } & AdditionalProps; + +export function SvgAst({ ast, override }: AstProps) { + if (!ast) { + return null; + } + const { props, children } = ast; + return ( + + {children} + + ); +} + +export const err = console.error.bind(console); + +export function SvgXml(props: XmlProps) { + const { onError = err, xml, override } = props; + const ast = useMemo(() => (xml !== null ? parse(xml) : null), [ + xml, + ]); + + try { + return ; + } catch (error) { + onError(error); + return null; + } +} + +export async function fetchText(uri: string, params = {}) { + const response = await fetch(uri, params); + return await response.text(); +} + +export function SvgUri(props: UriProps) { + const { onError = err, uri, headers } = props; + const [xml, setXml] = useState(null); + const params = headers ? { headers } : {} + useEffect(() => { + uri + ? fetchText(uri, params) + .then(setXml) + .catch(onError) + : setXml(null); + }, [onError, uri]); + return ; +} + +// Extending Component is required for Animated support. + +export class SvgFromXml extends Component { + state = { ast: null }; + componentDidMount() { + this.parse(this.props.xml); + } + componentDidUpdate(prevProps: { xml: string | null }) { + const { xml } = this.props; + if (xml !== prevProps.xml) { + this.parse(xml); + } + } + parse(xml: string | null) { + try { + this.setState({ ast: xml ? parse(xml) : null }); + } catch (e) { + console.error(e); + } + } + render() { + const { + props, + state: { ast }, + } = this; + return ; + } +} + +export class SvgFromUri extends Component { + state = { xml: null }; + componentDidMount() { + this.fetch(this.props.uri); + } + componentDidUpdate(prevProps: { uri: string | null }) { + const { uri } = this.props; + if (uri !== prevProps.uri) { + this.fetch(uri); + } + } + async fetch(uri: string | null) { + try { + this.setState({ xml: uri ? await fetchText(uri) : null }); + } catch (e) { + console.error(e); + } + } + render() { + const { + props, + state: { xml }, + } = this; + return ; + } +} + +const upperCase = (_match: string, letter: string) => letter.toUpperCase(); + +export const camelCase = (phrase: string) => + phrase.replace(/[:-]([a-z])/g, upperCase); + +export type Styles = { [property: string]: string }; + +export function getStyle(string: string): Styles { + const style: Styles = {}; + const declarations = string.split(';'); + const { length } = declarations; + for (let i = 0; i < length; i++) { + const declaration = declarations[i]; + if (declaration.length !== 0) { + const split = declaration.split(':'); + const property = split[0]; + const value = split[1]; + style[camelCase(property.trim())] = value.trim(); + } + } + return style; +} + +export function astToReact( + value: AST | string, + index: number, +): JSX.Element | string { + if (typeof value === 'object') { + const { Tag, props, children } = value; + return ( + + {(children as (AST | string)[]).map(astToReact)} + + ); + } + return value; +} + +// slimmed down parser based on https://github.com/Rich-Harris/svg-parser + +function repeat(str: string, i: number) { + let result = ''; + while (i--) { + result += str; + } + return result; +} + +const toSpaces = (tabs: string) => repeat(' ', tabs.length); + +function locate(source: string, i: number) { + const lines = source.split('\n'); + const nLines = lines.length; + let column = i; + let line = 0; + for (; line < nLines; line++) { + const { length } = lines[line]; + if (column >= length) { + column -= length; + } else { + break; + } + } + const before = source.slice(0, i).replace(/^\t+/, toSpaces); + const beforeExec = /(^|\n).*$/.exec(before); + const beforeLine = (beforeExec && beforeExec[0]) || ''; + const after = source.slice(i); + const afterExec = /.*(\n|$)/.exec(after); + const afterLine = afterExec && afterExec[0]; + const pad = repeat(' ', beforeLine.length); + const snippet = `${beforeLine}${afterLine}\n${pad}^`; + return { line, column, snippet }; +} + +const validNameCharacters = /[a-zA-Z0-9:_-]/; +const whitespace = /[\s\t\r\n]/; +const quotemarks = /['"]/; + +export type Middleware = (ast: XmlAST) => XmlAST; + +export function parse(source: string, middleware?: Middleware): JsxAST | null { + const length = source.length; + let currentElement: XmlAST | null = null; + let state = metadata; + let children = null; + let root: XmlAST | undefined; + let stack: XmlAST[] = []; + + function error(message: string) { + const { line, column, snippet } = locate(source, i); + throw new Error( + `${message} (${line}:${column}). If this is valid SVG, it's probably a bug. Please raise an issue\n\n${snippet}`, + ); + } + + function metadata() { + while ( + i + 1 < length && + (source[i] !== '<' || !validNameCharacters.test(source[i + 1])) + ) { + i++; + } + + return neutral(); + } + + function neutral() { + let text = ''; + let char; + while (i < length && (char = source[i]) !== '<') { + text += char; + i += 1; + } + + if (/\S/.test(text)) { + children.push(text); + } + + if (source[i] === '<') { + return openingTag; + } + + return neutral; + } + + function openingTag() { + const char = source[i]; + + if (char === '?') { + return neutral; + } // ') { + error('Expected >'); + } + + if (!selfClosing) { + currentElement = element; + ({ children } = element); + stack.push(element); + } + + return neutral; + } + + function comment() { + const index = source.indexOf('-->', i); + if (!~index) { + error('expected -->'); + } + + i = index + 2; + return neutral; + } + + function cdata() { + const index = source.indexOf(']]>', i); + if (!~index) { + error('expected ]]>'); + } + + children.push(source.slice(i + 7, index)); + + i = index + 2; + return neutral; + } + + function closingTag() { + const tag = getName(); + + if (!tag) { + error('Expected tag name'); + } + + if (currentElement && tag !== currentElement.tag) { + error( + `Expected closing tag to match opening tag <${currentElement.tag}>`, + ); + } + + if (source[i] !== '>') { + error('Expected >'); + } + + stack.pop(); + currentElement = stack[stack.length - 1]; + if (currentElement) { + ({ children } = currentElement); + } + + return neutral; + } + + function getName() { + let name = ''; + let char; + while (i < length && validNameCharacters.test((char = source[i]))) { + name += char; + i += 1; + } + + return name; + } + + function getAttributes(props: { + [x: string]: Styles | string | number | boolean | undefined; + style?: string | Styles | undefined; + }) { + while (i < length) { + if (!whitespace.test(source[i])) { + return; + } + allowSpaces(); + + const name = getName(); + if (!name) { + return; + } + + let value: boolean | number | string = true; + + allowSpaces(); + if (source[i] === '=') { + i += 1; + allowSpaces(); + + value = getAttributeValue(); + if (!isNaN(+value) && value.trim() !== '') { + value = +value; + } + } + + props[camelCase(name)] = value; + } + } + + function getAttributeValue(): string { + return quotemarks.test(source[i]) + ? getQuotedAttributeValue() + : getUnquotedAttributeValue(); + } + + function getUnquotedAttributeValue() { + let value = ''; + do { + const char = source[i]; + if (char === ' ' || char === '>' || char === '/') { + return value; + } + + value += char; + i += 1; + } while (i < length); + + return value; + } + + function getQuotedAttributeValue() { + const quotemark = source[i++]; + + let value = ''; + let escaped = false; + + while (i < length) { + const char = source[i++]; + if (char === quotemark && !escaped) { + return value; + } + + if (char === '\\' && !escaped) { + escaped = true; + } + + value += escaped ? `\\${char}` : char; + escaped = false; + } + + return value; + } + + function allowSpaces() { + while (i < length && whitespace.test(source[i])) { + i += 1; + } + } + + let i = 0; + while (i < length) { + if (!state) { + error('Unexpected character'); + } + state = state(); + i += 1; + } + + if (state !== neutral) { + error('Unexpected end of input'); + } + + if (root) { + const xml: XmlAST = (middleware ? middleware(root) : root) || root; + const ast: (JSX.Element | string)[] = xml.children.map(astToReact); + const jsx: JsxAST = xml as JsxAST; + jsx.children = ast; + return jsx; + } + + return null; +} diff --git a/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/build.gradle b/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/build.gradle new file mode 100644 index 0000000000..87ba617012 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/build.gradle @@ -0,0 +1,601 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +plugins { + id("com.android.library") + id("com.facebook.react.codegen") + id("maven-publish") + id("de.undercouch.download") +} + +import java.nio.file.Paths + +import de.undercouch.gradle.tasks.download.Download +import org.apache.tools.ant.taskdefs.condition.Os +import org.apache.tools.ant.filters.ReplaceTokens + +def AAR_OUTPUT_URL = "file://${projectDir}/../android" +// We download various C++ open-source dependencies into downloads. +// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk. +// After that we build native code from src/main/jni with module path pointing at third-party-ndk. + +def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR") +def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads") +def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") + +// You need to have following folders in this directory: +// - boost_1_63_0 +// - double-conversion-1.1.6 +// - folly-deprecate-dynamic-initializer +// - glog-0.3.5 +def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") + +// The Boost library is a very large download (>100MB). +// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable +// and the build will use that. +def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") + +// Setup build type for NDK, supported values: {debug, release} +def nativeBuildType = System.getenv("NATIVE_BUILD_TYPE") ?: "release" + +task createNativeDepsDirectories { + downloadsDir.mkdirs() + thirdPartyNdkDir.mkdirs() +} + +task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz")) +} + +task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) { + from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest))) + from("src/main/jni/third-party/boost/Android.mk") + from("src/main/jni/third-party/boost") + include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp", "asm/**/*.S") + includeEmptyDirs = false + into("$thirdPartyNdkDir/boost") + doLast { + file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}") + } +} + +task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz")) +} + +task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) { + from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest)) + from("src/main/jni/third-party/double-conversion/Android.mk") + include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk") + filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" }) + includeEmptyDirs = false + into("$thirdPartyNdkDir/double-conversion") +} + +task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")) +} + +def follyReplaceContent = ''' + ssize_t r; + do { + r = open(name, flags, mode); + } while (r == -1 && errno == EINTR); + return r; +''' + +task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) { + from(dependenciesPath ?: tarTree(downloadFolly.dest)) + from("src/main/jni/third-party/folly/Android.mk") + include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") + eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } + // Fixes problem with Folly failing to build on certain systems. See + // https://github.com/facebook/react-native/issues/28298 + filter { line -> line.replaceAll('return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);', follyReplaceContent) } + includeEmptyDirs = false + into("$thirdPartyNdkDir/folly") +} + +task downloadFmt(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/fmtlib/fmt/archive/${FMT_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "fmt-${FMT_VERSION}.tar.gz")) +} + +task prepareFmt(dependsOn: dependenciesPath ? [] : [downloadFmt], type: Copy) { + from(dependenciesPath ?: tarTree(downloadFmt.dest)) + from("src/main/jni/third-party/fmt/Android.mk") + include("fmt-${FMT_VERSION}/src/**/*", "fmt-${FMT_VERSION}/include/**/*", "Android.mk") + eachFile { fname -> fname.path = (fname.path - "fmt-${FMT_VERSION}/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/fmt") +} + +task downloadLibevent(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}-stable/libevent-${LIBEVENT_VERSION}-stable.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "libevent-${LIBEVENT_VERSION}.tar.gz")) +} + +task prepareLibevent(dependsOn: dependenciesPath ? [] : [downloadLibevent], type: Copy) { + from(dependenciesPath ?: tarTree(downloadLibevent.dest)) + from("src/main/jni/third-party/libevent/Android.mk") + from("src/main/jni/third-party/libevent/event-config.h") + from("src/main/jni/third-party/libevent/evconfig-private.h") + include( + "libevent-${LIBEVENT_VERSION}-stable/*.c", + "libevent-${LIBEVENT_VERSION}-stable/*.h", + "libevent-${LIBEVENT_VERSION}-stable/include/**/*", + "evconfig-private.h", + "event-config.h", + "Android.mk" + ) + eachFile { fname -> fname.path = (fname.path - "libevent-${LIBEVENT_VERSION}-stable/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/libevent") + + doLast { + ant.move(file: "$thirdPartyNdkDir/libevent/event-config.h", tofile: "$thirdPartyNdkDir/libevent/include/event2/event-config.h") + } +} + +task prepareHermes(dependsOn: createNativeDepsDirectories, type: Copy) { + def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") + if (!hermesPackagePath) { + throw new GradleScriptException("Could not find the hermes-engine npm package", null) + } + + def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar") + if (!hermesAAR.exists()) { + throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"", null) + } + + def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" }) + + from soFiles + from "src/main/jni/first-party/hermes/Android.mk" + into "$thirdPartyNdkDir/hermes" +} + +task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz")) +} + +// Prepare glog sources to be compiled, this task will perform steps that normally should've been +// executed by automake. This way we can avoid dependencies on make/automake +task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) { + from(dependenciesPath ?: tarTree(downloadGlog.dest)) + from("src/main/jni/third-party/glog/") + include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h") + includeEmptyDirs = false + filesMatching("**/*.h.in") { + filter(ReplaceTokens, tokens: [ + ac_cv_have_unistd_h : "1", + ac_cv_have_stdint_h : "1", + ac_cv_have_systypes_h : "1", + ac_cv_have_inttypes_h : "1", + ac_cv_have_libgflags : "0", + ac_google_start_namespace : "namespace google {", + ac_cv_have_uint16_t : "1", + ac_cv_have_u_int16_t : "1", + ac_cv_have___uint16 : "0", + ac_google_end_namespace : "}", + ac_cv_have___builtin_expect : "1", + ac_google_namespace : "google", + ac_cv___attribute___noinline : "__attribute__ ((noinline))", + ac_cv___attribute___noreturn : "__attribute__ ((noreturn))", + ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))" + ]) + it.path = (it.name - ".in") + } + into("$thirdPartyNdkDir/glog") + + doLast { + copy { + from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files) + includeEmptyDirs = false + into("$thirdPartyNdkDir/glog/exported/glog") + } + } +} + +// Create Android.mk library module based on jsc from npm +task prepareJSC { + doLast { + def jscPackagePath = findNodeModulePath(projectDir, "jsc-android") + if (!jscPackagePath) { + throw new GradleScriptException("Could not find the jsc-android npm package", null) + } + + def jscDist = file("$jscPackagePath/dist") + if (!jscDist.exists()) { + throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory", null) + } + + def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile + def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" }) + + def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" }) + + copy { + from(soFiles) + from(headerFiles) + from("src/main/jni/third-party/jsc/Android.mk") + + filesMatching("**/*.h", { it.path = "JavaScriptCore/${it.name}" }) + + includeEmptyDirs(false) + into("$thirdPartyNdkDir/jsc") + } + } +} +task downloadNdkBuildDependencies { + if (!boostPath) { + dependsOn(downloadBoost) + } + dependsOn(downloadDoubleConversion) + dependsOn(downloadFolly) + dependsOn(downloadGlog) + dependsOn(downloadFmt) + dependsOn(downloadLibevent) +} + +/** + * Finds the path of the installed npm package with the given name using Node's + * module resolution algorithm, which searches "node_modules" directories up to + * the file system root. This handles various cases, including: + * + * - Working in the open-source RN repo: + * Gradle: /path/to/react-native/ReactAndroid + * Node module: /path/to/react-native/node_modules/[package] + * + * - Installing RN as a dependency of an app and searching for hoisted + * dependencies: + * Gradle: /path/to/app/node_modules/react-native/ReactAndroid + * Node module: /path/to/app/node_modules/[package] + * + * - Working in a larger repo (e.g., Facebook) that contains RN: + * Gradle: /path/to/repo/path/to/react-native/ReactAndroid + * Node module: /path/to/repo/node_modules/[package] + * + * The search begins at the given base directory (a File object). The returned + * path is a string. + */ +def findNodeModulePath(baseDir, packageName) { + def basePath = baseDir.toPath().normalize() + // Node's module resolution algorithm searches up to the root directory, + // after which the base path will be null + while (basePath) { + def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName) + if (candidatePath.toFile().exists()) { + return candidatePath.toString() + } + basePath = basePath.getParent() + } + return null +} + +def getNdkBuildName() { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + return "ndk-build.cmd" + } else { + return "ndk-build" + } +} + +def findNdkBuildFullPath() { + // android.ndkDirectory should return project.android.ndkVersion ndkDirectory + def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null + if (ndkDir) { + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + // we allow to provide full path to ndk-build tool + if (hasProperty("ndk.command")) { + return property("ndk.command") + } + // or just a path to the containing directory + if (hasProperty("ndk.path")) { + ndkDir = property("ndk.path") + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + // @TODO ANDROID_NDK && ndk.dir is deprecated and will be removed in the future. + if (System.getenv("ANDROID_NDK") != null) { + ndkDir = System.getenv("ANDROID_NDK") + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + return null +} + +def reactNativeDevServerPort() { + def value = project.getProperties().get("reactNativeDevServerPort") + return value != null ? value : "8081" +} + +def reactNativeInspectorProxyPort() { + def value = project.getProperties().get("reactNativeInspectorProxyPort") + return value != null ? value : reactNativeDevServerPort() +} + +def reactNativeArchitectures() { + def isDebug = gradle.startParameter.taskRequests.any { + it.args.any { it.endsWith("Debug") } + } + def value = project.getProperties().get("reactNativeDebugArchitectures") + return value != null && isDebug ? value : "all" +} + +def getNdkBuildFullPath() { + def ndkBuildFullPath = findNdkBuildFullPath() + if (ndkBuildFullPath == null) { + throw new GradleScriptException( + "ndk-build binary cannot be found, check if you've set " + + "\$ANDROID_NDK environment variable correctly or if ndk.dir is " + + "setup in local.properties", + null) + } + if (!new File(ndkBuildFullPath).canExecute()) { + throw new GradleScriptException( + "ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" + + "Check that the \$ANDROID_NDK environment variable, or ndk.dir in local.properties, is set correctly.\n" + + "(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)", + null) + } + return ndkBuildFullPath +} + +def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { + dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFmt, prepareFolly, prepareGlog, prepareLibevent, extractAARHeaders, extractJNIFiles) + dependsOn("generateCodegenArtifactsFromSchema"); + + inputs.dir("$projectDir/../ReactCommon") + inputs.dir("src/main/jni") + inputs.dir("src/main/java/com/facebook/react/turbomodule/core/jni") + inputs.dir("src/main/java/com/facebook/react/modules/blob") + outputs.dir("$buildDir/react-ndk/all") + commandLine(getNdkBuildFullPath(), + "APP_ABI=${reactNativeArchitectures()}", + "NDK_DEBUG=" + (nativeBuildType.equalsIgnoreCase("debug") ? "1" : "0"), + "NDK_PROJECT_PATH=null", + "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", + "NDK_OUT=" + temporaryDir, + "NDK_LIBS_OUT=$buildDir/react-ndk/all", + "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", + "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", + "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", + "-C", file("src/main/jni/react/jni").absolutePath, + "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors() + ) +} + +def cleanReactNdkLib = tasks.register("cleanReactNdkLib", Exec) { + ignoreExitValue(true) + errorOutput(new ByteArrayOutputStream()) + commandLine(getNdkBuildFullPath(), + "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", + "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", + "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "-C", file("src/main/jni/react/jni").absolutePath, + "clean") + doLast { + file(AAR_OUTPUT_URL).delete() + println("Deleted aar output dir at ${file(AAR_OUTPUT_URL)}") + } +} + +def packageReactNdkLibs = tasks.register("packageReactNdkLibs", Copy) { + dependsOn(buildReactNdkLib) + from("$buildDir/react-ndk/all") + into("$buildDir/react-ndk/exported") + exclude("**/libjsc.so") + exclude("**/libhermes.so") +} + +def packageReactNdkLibsForBuck = tasks.register("packageReactNdkLibsForBuck", Copy) { + dependsOn(packageReactNdkLibs) + from("$buildDir/react-ndk/exported") + into("src/main/jni/prebuilt/lib") +} + +task extractAARHeaders { + doLast { + configurations.extractHeaders.files.each { + def file = it.absoluteFile + def packageName = file.name.tokenize('-')[0] + copy { + from zipTree(file) + into "$projectDir/src/main/jni/first-party/$packageName/headers" + include "**/*.h" + } + } + } +} + +task extractJNIFiles { + doLast { + configurations.extractJNI.files.each { + def file = it.absoluteFile + def packageName = file.name.tokenize('-')[0] + copy { + from zipTree(file) + into "$projectDir/src/main/jni/first-party/$packageName/" + include "jni/**/*" + } + } + } +} + +task installArchives { + dependsOn("publishReleasePublicationToNpmRepository") +} + +android { + compileSdkVersion 30 + ndkVersion ANDROID_NDK_VERSION + if (ANDROID_NDK_PATH != null) { + ndkPath ANDROID_NDK_PATH + } + + defaultConfig { + minSdkVersion(21) + targetSdkVersion(28) + versionCode(1) + versionName("1.0") + + consumerProguardFiles("proguard-rules.pro") + + buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") + buildConfigField("int", "EXOPACKAGE_FLAGS", "0") + buildConfigField("int", "HERMES_BYTECODE_VERSION", "0") + + resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() + resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() + + testApplicationId("com.facebook.react.tests.gradle") + testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner") + } + + sourceSets.main { + jni.srcDirs = [] + jniLibs.srcDir("$buildDir/react-ndk/exported") + res.srcDirs = ["src/main/res/devsupport", "src/main/res/shell", "src/main/res/views/modal", "src/main/res/views/uimanager"] + java { + srcDirs = ["src/main/java", "src/main/libraries/soloader/java", "src/main/jni/first-party/fb/jni/java"] + exclude("com/facebook/react/processing") + exclude("com/facebook/react/module/processing") + } + } + + preBuild.dependsOn(packageReactNdkLibs) + clean.dependsOn(cleanReactNdkLib) + + lintOptions { + abortOnError(false) + } + + packagingOptions { + exclude("META-INF/NOTICE") + exclude("META-INF/LICENSE") + } + + configurations { + extractHeaders + extractJNI + javadocDeps.extendsFrom api + } +} + +dependencies { + api("com.facebook.infer.annotation:infer-annotation:0.18.0") + api("com.facebook.yoga:proguard-annotations:1.19.0") + api("javax.inject:javax.inject:1") + api("androidx.appcompat:appcompat:1.0.2") + api("androidx.autofill:autofill:1.1.0") + api("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + api("com.facebook.fresco:fresco:${FRESCO_VERSION}") + api("com.facebook.fresco:imagepipeline-okhttp3:${FRESCO_OKHTTP_VERSION}") + api("com.facebook.fresco:ui-common:${FRESCO_VERSION}") + api("com.facebook.soloader:soloader:${SO_LOADER_VERSION}") + api("com.google.code.findbugs:jsr305:3.0.2") + api("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}") + api("com.squareup.okhttp3:okhttp-urlconnection:${OKHTTP_VERSION}") + api("com.squareup.okio:okio:2.9.0") + api("com.facebook.fbjni:fbjni-java-only:0.2.2") + extractHeaders("com.facebook.fbjni:fbjni:0.2.2:headers") + extractJNI("com.facebook.fbjni:fbjni:0.2.2") + + javadocDeps("com.squareup:javapoet:1.13.0") + + testImplementation("junit:junit:${JUNIT_VERSION}") + testImplementation("org.powermock:powermock-api-mockito2:${POWERMOCK_VERSION}") + testImplementation("org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}") + testImplementation("org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}") + testImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") + testImplementation("org.easytesting:fest-assert-core:2.0M10") + testImplementation("org.robolectric:robolectric:${ROBOLECTRIC_VERSION}") + + androidTestImplementation(fileTree(dir: "src/main/third-party/java/buck-android-support/", include: ["*.jar"])) + androidTestImplementation("androidx.test:runner:${ANDROIDX_TEST_VERSION}") + androidTestImplementation("androidx.test:rules:${ANDROIDX_TEST_VERSION}") + androidTestImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") +} + +react { + // TODO: The library name is chosen for parity with Fabric components & iOS + // This should be changed to a more generic name, e.g. `ReactCoreSpec`. + libraryName = "rncore" + jsRootDir = file("../Libraries") + reactNativeRootDir = file("$projectDir/..") + useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + // Applies the component for the release build variant. + from components.release + + // You can then customize attributes of the publication as shown below. + artifactId = POM_ARTIFACT_ID + groupId = GROUP + version = VERSION_NAME + + pom { + name = POM_NAME + description = "A framework for building native apps with React" + url = "https://github.com/facebook/react-native" + + developers { + developer { + id = "facebook" + name = "Facebook" + } + } + + licenses { + license { + name = "MIT License" + url = "https://github.com/facebook/react-native/blob/HEAD/LICENSE" + distribution = "repo" + } + } + + scm { + url = "https://github.com/facebook/react-native.git" + connection = "scm:git:https://github.com/facebook/react-native.git" + developerConnection = "scm:git:git@github.com:facebook/react-native.git" + } + } + } + } + + repositories { + maven { + name = "npm" + url = AAR_OUTPUT_URL + } + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/gradle.properties b/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/gradle.properties new file mode 100644 index 0000000000..50bdba905a --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-tvos/overrides@0.66.3-1/ReactAndroid/gradle.properties @@ -0,0 +1,27 @@ +VERSION_NAME=0.66.3-1 +GROUP=com.facebook.react + +POM_NAME=ReactNative +POM_ARTIFACT_ID=react-native +POM_PACKAGING=aar + +MOCKITO_CORE_VERSION=2.26.0 +POWERMOCK_VERSION=2.0.2 +ROBOLECTRIC_VERSION=4.4 +JUNIT_VERSION=4.12 + +ANDROIDX_TEST_VERSION=1.1.0 +FRESCO_VERSION=2.5.0 +FRESCO_OKHTTP_VERSION=3.14.9 +OKHTTP_VERSION=4.9.3 +SO_LOADER_VERSION=0.10.1 + +BOOST_VERSION=1_63_0 +DOUBLE_CONVERSION_VERSION=1.1.6 +FOLLY_VERSION=2021.06.28.00 +FMT_VERSION=6.2.1 +LIBEVENT_VERSION=2.1.12 +GLOG_VERSION=0.3.5 + +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/rnv/pluginTemplates/react-native-vector-icons/overrides/RNVectorIcons.podspec b/packages/rnv/pluginTemplates/react-native-vector-icons/overrides/RNVectorIcons.podspec index a0cf698e95..9746dc53f4 100644 --- a/packages/rnv/pluginTemplates/react-native-vector-icons/overrides/RNVectorIcons.podspec +++ b/packages/rnv/pluginTemplates/react-native-vector-icons/overrides/RNVectorIcons.podspec @@ -14,6 +14,6 @@ Pod::Spec.new do |s| s.source_files = 'RNVectorIconsManager/**/*.{h,m}' s.resources = "Fonts/*.ttf" s.preserve_paths = "**/*.js" - s.dependency 'React' + s.dependency 'React-Core' end diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/build.gradle new file mode 100644 index 0000000000..e83f677aa6 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/build.gradle @@ -0,0 +1,39 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.facebook.react:react-native:+' + implementation project(':react-native-webview') +} diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java new file mode 100644 index 0000000000..0777bb66cc --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java @@ -0,0 +1,78 @@ +package com.github.alinz.reactnativewebviewbridge; + +import android.webkit.WebView; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.reactnativecommunity.webview.RNCWebViewManager; + +import java.util.ArrayList; +import java.util.Map; + +import javax.annotation.Nullable; + +public class WebViewBridgeManager extends RNCWebViewManager { + private static final String REACT_CLASS = "RCTWebViewBridge"; + + public static final int COMMAND_SEND_TO_BRIDGE = 101; + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + public + @Nullable + Map getCommandsMap() { + Map commandsMap = super.getCommandsMap(); + + commandsMap.put("sendToBridge", COMMAND_SEND_TO_BRIDGE); + + return commandsMap; + } + + @Override + protected WebView createViewInstance(ThemedReactContext reactContext) { + WebView root = super.createViewInstance(reactContext); + root.addJavascriptInterface(new JavascriptBridge(root), "WebViewBridge"); + return root; + } + + @Override + public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { + super.receiveCommand(root, commandId, args); + + switch (commandId) { + case COMMAND_SEND_TO_BRIDGE: + sendToBridge(root, args.getString(0)); + break; + default: + //do nothing!!!! + } + } + + private void sendToBridge(WebView root, String message) { + String script = "WebViewBridge.onMessage('" + message + "');"; + WebViewBridgeManager.evaluateJavascript(root, script); + } + + static private void evaluateJavascript(WebView root, String javascript) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + root.evaluateJavascript(javascript, null); + } else { + root.loadUrl("javascript:" + javascript); + } + } + + @ReactProp(name = "allowFileAccessFromFileURLs") + public void setAllowFileAccessFromFileURLs(WebView root, boolean allows) { + root.getSettings().setAllowFileAccessFromFileURLs(allows); + } + + @ReactProp(name = "allowUniversalAccessFromFileURLs") + public void setAllowUniversalAccessFromFileURLs(WebView root, boolean allows) { + root.getSettings().setAllowUniversalAccessFromFileURLs(allows); + } +} diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java new file mode 100644 index 0000000000..f6323761f9 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java @@ -0,0 +1,26 @@ +package com.github.alinz.reactnativewebviewbridge; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class WebViewBridgePackage implements ReactPackage { + @Override + public List createNativeModules(ReactApplicationContext reactApplicationContext) { + return new ArrayList<>(); + } + + @Override + public List createViewManagers(ReactApplicationContext reactApplicationContext) { + return Arrays.asList( + new WebViewBridgeManager() + ); + } + + +} diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/ios/RCTWebViewBridge.m b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/ios/RCTWebViewBridge.m new file mode 100644 index 0000000000..2fb1430372 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/ios/RCTWebViewBridge.m @@ -0,0 +1,474 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz) + * All rights reserved + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + //AG + +#import "RCTWebViewBridge.h" + +#import + + +#import +#import +#import +#import +#import + +#import +#import +#import + +//This is a very elegent way of defining multiline string in objective-c. +//source: http://stackoverflow.com/a/23387659/828487 +#define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding] + +//we don'e need this one since it has been defined in RCTWebView.m +//NSString *const RCTJSNavigationScheme = @"react-js-navigation"; +NSString *const RCTJSNavigationScheme = @"react-js-navigation"; +NSString *const RCTWebViewBridgeSchema = @"__wvb__"; + +// runtime trick to remove UIWebview keyboard default toolbar +// see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279 +@interface _SwizzleHelper : NSObject @end +@implementation _SwizzleHelper +-(id)inputAccessoryView +{ + return nil; +} +@end + +@interface RCTWebViewBridge () + +@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingError; +@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest; +@property (nonatomic, copy) RCTDirectEventBlock onBridgeMessage; + +@end + +@implementation RCTWebViewBridge +{ + WKWebView *_webView; + NSString *_injectedJavaScript; + bool _shouldTrackLoadingStart; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + super.backgroundColor = [UIColor clearColor]; + _automaticallyAdjustContentInsets = YES; + _contentInset = UIEdgeInsetsZero; + _shouldTrackLoadingStart = NO; + [self setupWebview]; + [self addSubview:_webView]; + } + return self; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) + +- (void)goForward +{ + [_webView goForward]; +} + +- (void)goBack +{ + [_webView goBack]; +} + +- (void)reload +{ + [_webView reload]; +} + +- (void)sendToBridge:(NSString *)message +{ + //we are warpping the send message in a function to make sure that if + //WebView is not injected, we don't crash the app. + NSString *format = NSStringMultiline( + (function(){ + if (WebViewBridge && WebViewBridge.__push__) { + WebViewBridge.__push__('%@'); + } + }()); + ); + + NSString *command = [NSString stringWithFormat: format, message]; + [_webView evaluateJavaScript:command completionHandler:^(id result, NSError * _Nullable error) { + if (error) { + NSLog(@"WKWebview sendToBridge evaluateJavaScript Error: %@", error); + } + }]; +} + +- (NSURL *)URL +{ + return _webView.URL; +} + +- (void)setSource:(NSDictionary *)source +{ + if (![_source isEqualToDictionary:source]) { + _source = [source copy]; + + // Check for a static html source first + NSString *html = [RCTConvert NSString:source[@"html"]]; + if (html) { + NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]]; + [_webView loadHTMLString:html baseURL:baseURL]; + return; + } + + NSURLRequest *request = [RCTConvert NSURLRequest:source]; + // Because of the way React works, as pages redirect, we actually end up + // passing the redirect urls back here, so we ignore them if trying to load + // the same url. We'll expose a call to 'reload' to allow a user to load + // the existing page. + if ([request.URL isEqual:_webView.URL]) { + return; + } + if (!request.URL) { + // Clear the webview + [_webView loadHTMLString:@"" baseURL:nil]; + return; + } + [_webView loadRequest:request]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + _webView.frame = self.bounds; +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + _contentInset = contentInset; + [RCTView autoAdjustInsetsForView:self + withScrollView:_webView.scrollView + updateOffset:NO]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); + self.opaque = _webView.opaque = (alpha == 1.0); + _webView.backgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor +{ + return _webView.backgroundColor; +} + +- (NSMutableDictionary *)baseEvent +{ + NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"url": _webView.URL.absoluteString ?: @"", + @"loading" : @(_webView.loading), + @"title": _webView.title, + @"canGoBack": @(_webView.canGoBack), + @"canGoForward" : @(_webView.canGoForward), + }]; + + return event; +} + +- (void)refreshContentInset +{ + [RCTView autoAdjustInsetsForView:self + withScrollView:_webView.scrollView + updateOffset:YES]; +} + +-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView +{ + if (!hideKeyboardAccessoryView) { + return; + } + + UIView* subview; + for (UIView* view in _webView.scrollView.subviews) { + if([[view.class description] hasPrefix:@"UIWeb"]) + subview = view; + } + + if(subview == nil) return; + + NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelper", subview.class.superclass]; + Class newClass = NSClassFromString(name); + + if(newClass == nil) + { + newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); + if(!newClass) return; + + Method method = class_getInstanceMethod([_SwizzleHelper class], @selector(inputAccessoryView)); + class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); + + objc_registerClassPair(newClass); + } + + object_setClass(subview, newClass); +} + +#pragma mark - WebKit WebView Setup and JS Handler + +-(void)setupWebview { + WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init]; + WKUserContentController *controller = [[WKUserContentController alloc]init]; + [controller addScriptMessageHandler:self name:@"observe"]; + + [theConfiguration setUserContentController:controller]; + theConfiguration.allowsInlineMediaPlayback = NO; + + _webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:theConfiguration]; + _webView.UIDelegate = self; + _webView.navigationDelegate = self; + + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; +} + +-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ + if ([message.body rangeOfString:RCTWebViewBridgeSchema].location == NSNotFound) { + NSMutableDictionary *onBridgeMessageEvent = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"messages": [self stringArrayJsonToArray: message.body] + }]; + + _onBridgeMessage(onBridgeMessageEvent); + + return; + } + + [_webView evaluateJavaScript:@"WebViewBridge.__fetch__()" completionHandler:^(id result, NSError * _Nullable error) { + if (!error) { + NSMutableDictionary *onBridgeMessageEvent = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"messages": [self stringArrayJsonToArray: result] + }]; + + _onBridgeMessage(onBridgeMessageEvent); + } + }]; +} + +#pragma mark - WebKit WebView Delegate methods + +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation +{ + _shouldTrackLoadingStart = YES; +} + +-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ + if (_onLoadingStart && _shouldTrackLoadingStart) { + _shouldTrackLoadingStart = NO; + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": (navigationAction.request.URL).absoluteString, + @"navigationType": @(navigationAction.navigationType) + }]; + _onLoadingStart(event); + } + + if (_onShouldStartLoadWithRequest) { + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": (navigationAction.request.URL).absoluteString, + @"navigationType": @(navigationAction.navigationType) + }]; + + if (![self.delegate webView:self shouldStartLoadForRequest:event withCallback:_onShouldStartLoadWithRequest]) { + decisionHandler(WKNavigationActionPolicyCancel); + }else{ + decisionHandler(WKNavigationActionPolicyAllow); + } + } + decisionHandler(WKNavigationActionPolicyAllow); +} + +-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{ + if (_onLoadingError) { + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + // NSURLErrorCancelled is reported when a page has a redirect OR if you load + // a new URL in the WebView before the previous one came back. We can just + // ignore these since they aren't real errors. + // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os + return; + } + + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary:@{ + @"domain": error.domain, + @"code": @(error.code), + @"description": error.localizedDescription, + }]; + _onLoadingError(event); + } +} + +-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{ + NSString *webViewBridgeScriptContent = [self webViewBridgeScript]; + [webView evaluateJavaScript:webViewBridgeScriptContent completionHandler:^(id webviewScriptResult, NSError * _Nullable webviewScriptError) { + if (webviewScriptError) { + NSLog(@"WKWebview sendToBridge evaluateJavaScript Error: %@", webviewScriptError); + return; + } + + if (_injectedJavaScript != nil) { + [webView evaluateJavaScript:_injectedJavaScript completionHandler:^(id result, NSError * _Nullable error) { + NSString *jsEvaluationValue = (NSString *) result; + NSMutableDictionary *event = [self baseEvent]; + event[@"jsEvaluationValue"] = jsEvaluationValue; + if (_onLoadingFinish) { + _onLoadingFinish(event); + } + }]; + } else if (_onLoadingFinish) { + _onLoadingFinish([self baseEvent]); + } + }]; +} + +- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures +{ + + if (!navigationAction.targetFrame.isMainFrame) { + [webView loadRequest:navigationAction.request]; + } + + return nil; +} + +#pragma mark - WebviewBridge helpers + +- (NSArray*)stringArrayJsonToArray:(NSString *)message +{ + return [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] + options:NSJSONReadingAllowFragments + error:nil]; +} + +//since there is no easy way to load the static lib resource in ios, +//we are loading the script from this method. +- (NSString *)webViewBridgeScript { + // NSBundle *bundle = [NSBundle mainBundle]; + // NSString *webViewBridgeScriptFile = [bundle pathForResource:@"webviewbridge" + // ofType:@"js"]; + // NSString *webViewBridgeScriptContent = [NSString stringWithContentsOfFile:webViewBridgeScriptFile + // encoding:NSUTF8StringEncoding + // error:nil]; + + return NSStringMultiline( + (function (window) { + //Make sure that if WebViewBridge already in scope we don't override it. + if (window.WebViewBridge) { + return; + } + + var RNWBSchema = 'wvb'; + var sendQueue = []; + var receiveQueue = []; + var doc = window.document; + var customEvent = doc.createEvent('Event'); + + function wkWebViewBridgeAvailable() { + return ( + window.webkit && + window.webkit.messageHandlers && + window.webkit.messageHandlers.observe && + window.webkit.messageHandlers.observe.postMessage + ); + } + + function wkWebViewSend(event) { + if (!wkWebViewBridgeAvailable()) { + return; + } + try { + window.webkit.messageHandlers.observe.postMessage(event); + } catch (e) { + console.error('wkWebViewSend error', e.message); + if (window.WebViewBridge.onError) { + window.WebViewBridge.onError(e); + } + } + } + + function callFunc(func, message) { + if ('function' === typeof func) { + func(message); + } + } + + function signalNative() { + if (wkWebViewBridgeAvailable()) { + var event = window.WebViewBridge.__fetch__(); + wkWebViewSend(event); + } else { // iOS UIWebview + window.location = RNWBSchema + '://message' + new Date().getTime(); + } + } + + //I made the private function ugly signiture so user doesn't called them accidently. + //if you do, then I have nothing to say. :( + var WebViewBridge = { + //this function will be called by native side to push a new message + //to webview. + __push__: function (message) { + receiveQueue.push(message); + //reason I need this setTmeout is to return this function as fast as + //possible to release the native side thread. + setTimeout(function () { + var message = receiveQueue.pop(); + callFunc(WebViewBridge.onMessage, message); + }, 15); //this magic number is just a random small value. I don't like 0. + }, + __fetch__: function () { + //since our sendQueue array only contains string, and our connection to native + //can only accept string, we need to convert array of strings into single string. + var messages = JSON.stringify(sendQueue); + + //we make sure that sendQueue is resets + sendQueue = []; + + //return the messages back to native side. + return messages; + }, + //make sure message is string. because only string can be sent to native, + //if you don't pass it as string, onError function will be called. + send: function (message) { + if ('string' !== typeof message) { + callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string"); + return; + } + + //we queue the messages to make sure that native can collects all of them in one shot. + sendQueue.push(message); + //signal the objective-c that there is a message in the queue + signalNative(); + }, + onMessage: null, + onError: null + }; + + window.WebViewBridge = WebViewBridge; + + //dispatch event + customEvent.initEvent('WebViewBridge', true, true); + doc.dispatchEvent(customEvent); + })(this); + ); +} + +@end diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/react-native-webview-bridge.podspec b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/react-native-webview-bridge.podspec new file mode 100644 index 0000000000..42e17be04f --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/react-native-webview-bridge.podspec @@ -0,0 +1,20 @@ +require 'json' +version = JSON.parse(File.read('package.json'))["version"] + +Pod::Spec.new do |s| + + s.name = "react-native-webview-bridge" + s.version = version + s.homepage = "https://github.com/alinz/react-native-webview-bridge" + s.summary = "A webview bridge for react-native" + s.license = "MIT" + s.author = { "aurimas535" => "aurimas.mickys@gmail.com" } + s.ios.deployment_target = '7.0' + s.tvos.deployment_target = '9.0' + s.source = { :git => "https://github.com/cj3g10/react-native-webview-bridge", :tag => "#{s.version}" } + s.source_files = 'ios/*.{h,m}' + s.preserve_paths = "**/*.js" + s.frameworks = 'UIKit', 'QuartzCore', 'Foundation' + + s.dependency 'React' +end diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.android.js b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.android.js new file mode 100644 index 0000000000..43754b48c1 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.android.js @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * Copyright (c) 2016-present, Ali Najafizadeh + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule WebViewBridge + */ + + +const React = require('react'); +const PropTypes = require('prop-types'); +const ReactNative = require('react-native'); +const createReactClass = require('create-react-class'); +const invariant = require('invariant'); +const keyMirror = require('keymirror'); +const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); + +const WebView = require('react-native-webview'); + +const { + ReactNativeViewAttributes, + UIManager, + EdgeInsetsPropType, + StyleSheet, + Text, + View, + ViewPropTypes, + requireNativeComponent, + DeviceEventEmitter, + NativeModules: { + WebViewBridgeManager + } +} = ReactNative; + +const RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; + +const WebViewBridgeState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +const RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge); + +/** + * Renders a native WebView. + */ +var WebViewBridge = createReactClass({ + + propTypes: { + ...RCTWebViewBridge.propTypes, + + /** + * Will be called once the message is being sent from webview + */ + onBridgeMessage: PropTypes.func, + }, + + getInitialState() { + return { + viewState: WebViewBridgeState.IDLE, + lastErrorEvent: null, + startInLoadingState: true, + }; + }, + + + componentDidMount() { + DeviceEventEmitter.addListener('webViewBridgeMessage', (body) => { + const { onBridgeMessage } = this.props; + const { message } = body; + if (onBridgeMessage) { + onBridgeMessage(message); + } + }); + + if (this.props.startInLoadingState) { + this.setState({ viewState: WebViewBridgeState.LOADING }); + } + }, + + render() { + let otherView = null; + + if (this.state.viewState === WebViewBridgeState.LOADING) { + otherView = this.props.renderLoading && this.props.renderLoading(); + } else if (this.state.viewState === WebViewBridgeState.ERROR) { + const errorEvent = this.state.lastErrorEvent; + otherView = this.props.renderError && this.props.renderError( + errorEvent.domain, + errorEvent.code, + errorEvent.description + ); + } else if (this.state.viewState !== WebViewBridgeState.IDLE) { + console.error(`RCTWebViewBridge invalid state encountered: ${this.state.loading}`); + } + + const webViewStyles = [styles.container, this.props.style]; + if (this.state.viewState === WebViewBridgeState.LOADING + || this.state.viewState === WebViewBridgeState.ERROR) { + // if we're in either LOADING or ERROR states, don't show the webView + webViewStyles.push(styles.hidden); + } + + let { javaScriptEnabled, domStorageEnabled } = this.props; + if (this.props.javaScriptEnabledAndroid) { + console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); + javaScriptEnabled = this.props.javaScriptEnabledAndroid; + } + if (this.props.domStorageEnabledAndroid) { + console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); + domStorageEnabled = this.props.domStorageEnabledAndroid; + } + + const { source, ...props } = { ...this.props }; + + const webView = ( + + ); + + return ( + + {webView} + {otherView} + + ); + }, + + onMessage(event) { + if (this.props.onBridgeMessage != null && event.nativeEvent != null) { + this.props.onBridgeMessage(event.nativeEvent.message); + } + }, + + goForward() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goForward, + null + ); + }, + + goBack() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goBack, + null + ); + }, + + reload() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.reload, + null + ); + }, + + sendToBridge(message: string) { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.sendToBridge, + [message] + ); + }, + + /** + * We return an event with a bunch of fields including: + * url, title, loading, canGoBack, canGoForward + */ + updateNavigationState(event) { + if (this.props.onNavigationStateChange) { + this.props.onNavigationStateChange(event.nativeEvent); + } + }, + + getWebViewBridgeHandle() { + return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); + }, + + onLoadingStart(event) { + const { onLoadStart } = this.props; + onLoadStart && onLoadStart(event); + this.updateNavigationState(event); + }, + + onLoadingError(event) { + event.persist(); // persist this event because we need to store it + const { onError, onLoadEnd } = this.props; + onError && onError(event); + onLoadEnd && onLoadEnd(event); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewBridgeState.ERROR + }); + }, + + onLoadingFinish(event) { + const { onLoad, onLoadEnd } = this.props; + onLoad && onLoad(event); + onLoadEnd && onLoadEnd(event); + this.setState({ + viewState: WebViewBridgeState.IDLE, + }); + this.updateNavigationState(event); + }, +}); + + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, +}); + +module.exports = WebViewBridge; diff --git a/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.ios.js b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.ios.js new file mode 100644 index 0000000000..d25f87b942 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-webview-bridge/overrides/webview-bridge/index.ios.js @@ -0,0 +1,329 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * Copyright (c) 2016-present, Ali Najafizadeh + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule WebViewBridge + * @flow + */ + + +const React = require('react'); +const PropTypes = require('prop-types'); +const ReactNative = require('react-native'); +const createReactClass = require('create-react-class'); +const invariant = require('invariant'); +const keyMirror = require('keymirror'); +const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); + +const WebView = require('react-native-webview'); + +const { + ActivityIndicator, + EdgeInsetsPropType, + StyleSheet, + Text, + View, + ViewPropTypes, + requireNativeComponent, + UIManager, + NativeModules: { + WebViewBridgeManager, + }, +} = ReactNative; + +const BGWASH = 'rgba(255,255,255,0.8)'; +const RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; + +const RCTWebViewBridgeManager = WebViewBridgeManager; + +const WebViewBridgeState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +const NavigationType = { + click: RCTWebViewBridgeManager.NavigationType.LinkClicked, + formsubmit: RCTWebViewBridgeManager.NavigationType.FormSubmitted, + backforward: RCTWebViewBridgeManager.NavigationType.BackForward, + reload: RCTWebViewBridgeManager.NavigationType.Reload, + formresubmit: RCTWebViewBridgeManager.NavigationType.FormResubmitted, + other: RCTWebViewBridgeManager.NavigationType.Other, +}; + +const { JSNavigationScheme } = RCTWebViewBridgeManager; + +type ErrorEvent = { + domain: any; + code: any; + description: any; +} + +type Event = Object; + +const defaultRenderLoading = () => ( + + + +); +const defaultRenderError = (errorDomain, errorCode, errorDesc) => ( + + + Error loading page + + + {`Domain: ${errorDomain}`} + + + {`Error Code: ${errorCode}`} + + + {`Description: ${errorDesc}`} + + +); + +/** + * Renders a native WebView. + */ +const WebViewBridge = createReactClass({ + statics: { + JSNavigationScheme, + NavigationType, + }, + + propTypes: { + ...WebView.propTypes, + + /** + * Will be called once the message is being sent from webview + */ + onBridgeMessage: PropTypes.func, + + hideKeyboardAccessoryView: PropTypes.bool, + + keyboardDisplayRequiresUserAction: PropTypes.bool, + }, + + getInitialState() { + return { + viewState: WebViewBridgeState.IDLE, + lastErrorEvent: (null: ?ErrorEvent), + startInLoadingState: true, + }; + }, + + componentDidMount() { + if (this.props.startInLoadingState) { + this.setState({ viewState: WebViewBridgeState.LOADING }); + } + }, + + render() { + let otherView = null; + + if (this.state.viewState === WebViewBridgeState.LOADING) { + otherView = (this.props.renderLoading || defaultRenderLoading)(); + } else if (this.state.viewState === WebViewBridgeState.ERROR) { + const errorEvent = this.state.lastErrorEvent; + invariant( + errorEvent != null, + 'lastErrorEvent expected to be non-null' + ); + otherView = (this.props.renderError || defaultRenderError)( + errorEvent.domain, + errorEvent.code, + errorEvent.description + ); + } else if (this.state.viewState !== WebViewBridgeState.IDLE) { + console.error( + `RCTWebViewBridge invalid state encountered: ${this.state.loading}` + ); + } + + const webViewStyles = [styles.container, styles.webView, this.props.style]; + if (this.state.viewState === WebViewBridgeState.LOADING + || this.state.viewState === WebViewBridgeState.ERROR) { + // if we're in either LOADING or ERROR states, don't show the webView + webViewStyles.push(styles.hidden); + } + + const onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => { + const shouldStart = this.props.onShouldStartLoadWithRequest + && this.props.onShouldStartLoadWithRequest(event.nativeEvent); + RCTWebViewBridgeManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); + }); + + let { javaScriptEnabled, domStorageEnabled } = this.props; + if (this.props.javaScriptEnabledAndroid) { + console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); + javaScriptEnabled = this.props.javaScriptEnabledAndroid; + } + if (this.props.domStorageEnabledAndroid) { + console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); + domStorageEnabled = this.props.domStorageEnabledAndroid; + } + + const onBridgeMessage = (event: Event) => { + const onBridgeMessageCallback = this.props.onBridgeMessage; + if (onBridgeMessageCallback) { + const { messages } = event.nativeEvent; + if (messages && typeof messages.forEach === 'function') { + messages.forEach((message) => { + onBridgeMessageCallback(message); + }); + } + } + }; + + const { source, ...props } = { ...this.props }; + delete props.onBridgeMessage; + delete props.onShouldStartLoadWithRequest; + delete props.scalesPageToFit; + + const webView = ( + + ); + + return ( + + {webView} + {otherView} + + ); + }, + + goForward() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goForward, + null + ); + }, + + goBack() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goBack, + null + ); + }, + + reload() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.reload, + null + ); + }, + + sendToBridge(message: string) { + WebViewBridgeManager.sendToBridge(this.getWebViewBridgeHandle(), message); + }, + + /** + * We return an event with a bunch of fields including: + * url, title, loading, canGoBack, canGoForward + */ + updateNavigationState(event: Event) { + if (this.props.onNavigationStateChange) { + this.props.onNavigationStateChange(event.nativeEvent); + } + }, + + getWebViewBridgeHandle(): any { + return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); + }, + + onLoadingStart(event: Event) { + const { onLoadStart } = this.props; + onLoadStart && onLoadStart(event); + this.updateNavigationState(event); + }, + + onLoadingError(event: Event) { + event.persist(); // persist this event because we need to store it + const { onError, onLoadEnd } = this.props; + onError && onError(event); + onLoadEnd && onLoadEnd(event); + console.warn('Encountered an error loading page', event.nativeEvent); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewBridgeState.ERROR, + }); + }, + + onLoadingFinish(event: Event) { + const { onLoad, onLoadEnd } = this.props; + onLoad && onLoad(event); + onLoadEnd && onLoadEnd(event); + this.setState({ + viewState: WebViewBridgeState.IDLE, + }); + this.updateNavigationState(event); + }, +}); + +const RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge, { + nativeOnly: { + onLoadingStart: true, + onLoadingError: true, + onLoadingFinish: true, + }, +}); + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + errorContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: BGWASH, + }, + errorText: { + fontSize: 14, + textAlign: 'center', + marginBottom: 2, + }, + errorTextTitle: { + fontSize: 15, + fontWeight: '500', + marginBottom: 10, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, + loadingView: { + backgroundColor: BGWASH, + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + webView: { + backgroundColor: '#ffffff', + }, +}); + +module.exports = WebViewBridge; diff --git a/packages/rnv/pluginTemplates/react-native-zip-archive/overrides/android/build.gradle b/packages/rnv/pluginTemplates/react-native-zip-archive/overrides/android/build.gradle index c727f4605a..00e6b64302 100644 --- a/packages/rnv/pluginTemplates/react-native-zip-archive/overrides/android/build.gradle +++ b/packages/rnv/pluginTemplates/react-native-zip-archive/overrides/android/build.gradle @@ -1,7 +1,7 @@ buildscript { repositories { - jcenter() google() + mavenCentral() } dependencies { @@ -12,12 +12,17 @@ buildscript { apply plugin: 'com.android.library' android { - buildToolsVersion "26.0.1" - compileSdkVersion 26 + compileSdkVersion 33 + buildToolsVersion '28.0.0' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } defaultConfig { - minSdkVersion 16 - targetSdkVersion 26 + minSdkVersion 26 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -32,5 +37,5 @@ repositories { dependencies { implementation "com.facebook.react:react-native:+" - implementation files('libs/zip4j-1.3.3.jar') + implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.+' } diff --git a/packages/rnv/pluginTemplates/react-native-zip-archive/overrides@4.0.2/android/build.gradle b/packages/rnv/pluginTemplates/react-native-zip-archive/overrides@4.0.2/android/build.gradle new file mode 100644 index 0000000000..c727f4605a --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-zip-archive/overrides@4.0.2/android/build.gradle @@ -0,0 +1,36 @@ +buildscript { + repositories { + jcenter() + google() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +apply plugin: 'com.android.library' + +android { + buildToolsVersion "26.0.1" + compileSdkVersion 26 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation "com.facebook.react:react-native:+" + implementation files('libs/zip4j-1.3.3.jar') +} diff --git a/packages/rnv/pluginTemplates/react-native-zss-rich-text-editor/overrides/src/RichTextToolbar.js b/packages/rnv/pluginTemplates/react-native-zss-rich-text-editor/overrides/src/RichTextToolbar.js new file mode 100644 index 0000000000..684eb484ce --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native-zss-rich-text-editor/overrides/src/RichTextToolbar.js @@ -0,0 +1,194 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { FlatList, View, TouchableOpacity, Image, StyleSheet } from 'react-native'; +import { actions } from './const'; + +const defaultActions = [ + actions.insertImage, + actions.setBold, + actions.setItalic, + actions.insertBulletsList, + actions.insertOrderedList, + actions.insertLink +]; + +function getDefaultIcon() { + const texts = {}; + texts[actions.insertImage] = require('../img/icon_format_media.png'); + texts[actions.setBold] = require('../img/icon_format_bold.png'); + texts[actions.setItalic] = require('../img/icon_format_italic.png'); + texts[actions.insertBulletsList] = require('../img/icon_format_ul.png'); + texts[actions.insertOrderedList] = require('../img/icon_format_ol.png'); + texts[actions.insertLink] = require('../img/icon_format_link.png'); + return texts; +} + + +export default class RichTextToolbar extends Component { + + static propTypes = { + getEditor: PropTypes.func.isRequired, + actions: PropTypes.array, + onPressAddLink: PropTypes.func, + onPressAddImage: PropTypes.func, + selectedButtonStyle: PropTypes.object, + iconTint: PropTypes.any, + selectedIconTint: PropTypes.any, + unselectedButtonStyle: PropTypes.object, + renderAction: PropTypes.func, + iconMap: PropTypes.object, + }; + + constructor(props) { + super(props); + const actions = this.props.actions ? this.props.actions : defaultActions; + this.state = { + editor: undefined, + selectedItems: [], + actions, + dataSet: this.getRows(actions, []) + }; + } + + componentDidReceiveProps(newProps) { + const actions = newProps.actions ? newProps.actions : defaultActions; + this.setState({ + actions, + dataSet: this.getRows(actions, this.state.selectedItems) + }); + } + + getRows(actions, selectedItems) { + return actions.map((action) => { return { action, selected: selectedItems.includes(action) }; }); + } + + componentDidMount() { + const editor = this.props.getEditor(); + if (!editor) { + throw new Error('Toolbar has no editor!'); + } else { + editor.registerToolbar((selectedItems) => this.setSelectedItems(selectedItems)); + this.setState({ editor }); + } + } + + setSelectedItems(selectedItems) { + if (selectedItems !== this.state.selectedItems) { + this.setState({ + selectedItems, + dataSet: this.getRows(this.state.actions, selectedItems) + }); + } + } + + _getButtonSelectedStyle() { + return this.props.selectedButtonStyle ? this.props.selectedButtonStyle : styles.defaultSelectedButton; + } + + _getButtonUnselectedStyle() { + return this.props.unselectedButtonStyle ? this.props.unselectedButtonStyle : styles.defaultUnselectedButton; + } + + _getButtonIcon(action) { + if (this.props.iconMap && this.props.iconMap[action]) { + return this.props.iconMap[action]; + } else if (getDefaultIcon()[action]) { + return getDefaultIcon()[action]; + } else { + return undefined; + } + } + + _defaultRenderAction(action, selected) { + const icon = this._getButtonIcon(action); + return ( + this._onPress(action)} + > + {icon ? : null} + + ); + } + + _renderAction(action, selected) { + return this.props.renderAction ? + this.props.renderAction(action, selected) : + this._defaultRenderAction(action, selected); + } + + render() { + return ( + + this._renderAction(item.item.action, item.item.selected)} + /> + + ) + } + + _onPress(action) { + switch (action) { + case actions.setBold: + case actions.setItalic: + case actions.insertBulletsList: + case actions.insertOrderedList: + case actions.setUnderline: + case actions.heading1: + case actions.heading2: + case actions.heading3: + case actions.heading4: + case actions.heading5: + case actions.heading6: + case actions.setParagraph: + case actions.removeFormat: + case actions.alignLeft: + case actions.alignCenter: + case actions.alignRight: + case actions.alignFull: + case actions.setSubscript: + case actions.setSuperscript: + case actions.setStrikethrough: + case actions.setHR: + case actions.setIndent: + case actions.setOutdent: + this.state.editor._sendAction(action); + break; + case actions.insertLink: + this.state.editor.prepareInsert(); + if (this.props.onPressAddLink) { + this.props.onPressAddLink(); + } else { + this.state.editor.getSelectedText().then(selectedText => { + this.state.editor.showLinkDialog(selectedText); + }); + } + break; + case actions.insertImage: + this.state.editor.prepareInsert(); + if (this.props.onPressAddImage) { + this.props.onPressAddImage(); + } + break; + case actions.openCamera: + this.state.editor.prepareInsert(); + if (this.props.onPressOpenCamera) { + this.props.onPressOpenCamera(); + } + break; + } + } +} + +const styles = StyleSheet.create({ + defaultSelectedButton: { + backgroundColor: 'red' + }, + defaultUnselectedButton: {} +}); diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TVTouchable.js b/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TVTouchable.js new file mode 100644 index 0000000000..01e51ba766 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TVTouchable.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import invariant from 'invariant'; +import ReactNative from '../../Renderer/shims/ReactNative'; +import type { + BlurEvent, + FocusEvent, + PressEvent, +} from '../../Types/CoreEventTypes'; +import Platform from '../../Utilities/Platform'; +import TVEventHandler from '../../Components/AppleTV/TVEventHandler'; + +type TVTouchableConfig = $ReadOnly<{| + getDisabled: () => boolean, + onBlur: (event: BlurEvent) => mixed, + onFocus: (event: FocusEvent) => mixed, + onPress: (event: PressEvent) => mixed, +|}>; + +export default class TVTouchable { + _tvEventHandler: TVEventHandler; + + constructor(component: any, config: TVTouchableConfig) { + invariant(Platform.isTV, 'TVTouchable: Requires `Platform.isTV`.'); + this._tvEventHandler = new TVEventHandler(); + this._tvEventHandler.enable(component, (_, tvData) => { + tvData.dispatchConfig = {}; + if (ReactNative.findNodeHandle(component) === tvData.tag) { + if (tvData.eventType === 'focus') { + config.onFocus(tvData); + } else if (tvData.eventType === 'blur') { + config.onBlur(tvData); + } else if (tvData.eventType === 'select') { + if (!config.getDisabled()) { + config.onPress(tvData); + } + } + } + }); + } + + destroy(): void { + this._tvEventHandler.disable(); + } +} diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TouchableOpacity.js b/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TouchableOpacity.js new file mode 100644 index 0000000000..6f564b2819 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.63.2/Libraries/Components/Touchable/TouchableOpacity.js @@ -0,0 +1,303 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import Pressability, { + type PressabilityConfig, +} from '../../Pressability/Pressability'; +import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; +import TVTouchable from './TVTouchable'; +import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; +import Animated from 'react-native/Libraries/Animated/src/Animated'; +import Easing from 'react-native/Libraries/Animated/src/Easing'; +import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; +import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle'; +import Platform from '../../Utilities/Platform'; +import * as React from 'react'; + +type TVProps = $ReadOnly<{| + hasTVPreferredFocus?: ?boolean, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, +|}>; + +type Props = $ReadOnly<{| + ...React.ElementConfig, + ...TVProps, + + activeOpacity?: ?number, + style?: ?ViewStyleProp, + + hostRef: React.Ref, +|}>; + +type State = $ReadOnly<{| + anim: Animated.Value, + pressability: Pressability, +|}>; + +/** + * A wrapper for making views respond properly to touches. + * On press down, the opacity of the wrapped view is decreased, dimming it. + * + * Opacity is controlled by wrapping the children in an Animated.View, which is + * added to the view hierarchy. Be aware that this can affect layout. + * + * Example: + * + * ``` + * renderButton: function() { + * return ( + * + * + * + * ); + * }, + * ``` + * ### Example + * + * ```ReactNativeWebPlayer + * import React, { Component } from 'react' + * import { + * AppRegistry, + * StyleSheet, + * TouchableOpacity, + * Text, + * View, + * } from 'react-native' + * + * class App extends Component { + * state = { count: 0 } + * + * onPress = () => { + * this.setState(state => ({ + * count: state.count + 1 + * })); + * }; + * + * render() { + * return ( + * + * + * Touch Here + * + * + * + * { this.state.count !== 0 ? this.state.count: null} + * + * + * + * ) + * } + * } + * + * const styles = StyleSheet.create({ + * container: { + * flex: 1, + * justifyContent: 'center', + * paddingHorizontal: 10 + * }, + * button: { + * alignItems: 'center', + * backgroundColor: '#DDDDDD', + * padding: 10 + * }, + * countContainer: { + * alignItems: 'center', + * padding: 10 + * }, + * countText: { + * color: '#FF00FF' + * } + * }) + * + * AppRegistry.registerComponent('App', () => App) + * ``` + * + */ +class TouchableOpacity extends React.Component { + _tvTouchable: ?TVTouchable; + + state: State = { + anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), + pressability: new Pressability(this._createPressabilityConfig()), + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + minPressDuration: 0, + pressRectOffset: this.props.pressRetentionOffset, + onBlur: event => { + if (Platform.isTV) { + this._opacityInactive(250); + } + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (Platform.isTV) { + this._opacityActive(150); + } + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, + onPressIn: event => { + this._opacityActive( + event.dispatchConfig.registrationName === 'onResponderGrant' + ? 0 + : 150, + ); + if (this.props.onPressIn != null) { + this.props.onPressIn(event); + } + }, + onPressOut: event => { + this._opacityInactive(250); + if (this.props.onPressOut != null) { + this.props.onPressOut(event); + } + }, + }; + } + + /** + * Animate the touchable to a new opacity. + */ + _setOpacityTo(toValue: number, duration: number): void { + Animated.timing(this.state.anim, { + toValue, + duration, + easing: Easing.inOut(Easing.quad), + useNativeDriver: Platform.OS !== 'android', + }).start(); + } + + _opacityActive(duration: number): void { + this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration); + } + + _opacityInactive(duration: number): void { + this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration); + } + + _getChildStyleOpacityWithDefault(): number { + const opacity = flattenStyle(this.props.style)?.opacity; + return typeof opacity === 'number' ? opacity : 1; + } + + render(): React.Node { + // BACKWARD-COMPATIBILITY: Focus and blur events were never supported before + // adopting `Pressability`, so preserve that behavior. + const { + onBlur, + onFocus, + ...eventHandlersWithoutBlurAndFocus + } = this.state.pressability.getEventHandlers(); + + return ( + + {this.props.children} + {__DEV__ ? ( + + ) : null} + + ); + } + + componentDidMount(): void { + if (Platform.isTV) { + this._tvTouchable = new TVTouchable(this, { + getDisabled: () => this.props.disabled === true, + onBlur: event => { + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onPress: event => { + if (this.props.onPress != null) { + this.props.onPress(event); + } + }, + }); + } + } + + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + if (this.props.disabled !== prevProps.disabled) { + this._opacityInactive(250); + } + } + + componentWillUnmount(): void { + if (Platform.isTV) { + if (this._tvTouchable != null) { + this._tvTouchable.destroy(); + } + } + this.state.pressability.reset(); + } +} + +module.exports = (React.forwardRef((props, hostRef) => ( + +)): React.ComponentType<$ReadOnly<$Diff>>); diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/Libraries/Components/Touchable/TouchableOpacity.js b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/Libraries/Components/Touchable/TouchableOpacity.js new file mode 100644 index 0000000000..a3fa8c79f6 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/Libraries/Components/Touchable/TouchableOpacity.js @@ -0,0 +1,286 @@ +// Override _setOpacityTo and set useNativeDriver false on android platforms due to crash happening + +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + + import Pressability, { + type PressabilityConfig, + } from '../../Pressability/Pressability'; + import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; + import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; + import Animated from 'react-native/Libraries/Animated/Animated'; + import Easing from 'react-native/Libraries/Animated/Easing'; + import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; + import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle'; + import Platform from '../../Utilities/Platform'; + import * as React from 'react'; + + type TVProps = $ReadOnly<{| + hasTVPreferredFocus?: ?boolean, + nextFocusDown?: ?number, + nextFocusForward?: ?number, + nextFocusLeft?: ?number, + nextFocusRight?: ?number, + nextFocusUp?: ?number, + |}>; + + type Props = $ReadOnly<{| + ...React.ElementConfig, + ...TVProps, + + activeOpacity?: ?number, + style?: ?ViewStyleProp, + + hostRef?: ?React.Ref, + |}>; + + type State = $ReadOnly<{| + anim: Animated.Value, + pressability: Pressability, + |}>; + + /** + * A wrapper for making views respond properly to touches. + * On press down, the opacity of the wrapped view is decreased, dimming it. + * + * Opacity is controlled by wrapping the children in an Animated.View, which is + * added to the view hierarchy. Be aware that this can affect layout. + * + * Example: + * + * ``` + * renderButton: function() { + * return ( + * + * + * + * ); + * }, + * ``` + * ### Example + * + * ```ReactNativeWebPlayer + * import React, { Component } from 'react' + * import { + * AppRegistry, + * StyleSheet, + * TouchableOpacity, + * Text, + * View, + * } from 'react-native' + * + * class App extends Component { + * state = { count: 0 } + * + * onPress = () => { + * this.setState(state => ({ + * count: state.count + 1 + * })); + * }; + * + * render() { + * return ( + * + * + * Touch Here + * + * + * + * { this.state.count !== 0 ? this.state.count: null} + * + * + * + * ) + * } + * } + * + * const styles = StyleSheet.create({ + * container: { + * flex: 1, + * justifyContent: 'center', + * paddingHorizontal: 10 + * }, + * button: { + * alignItems: 'center', + * backgroundColor: '#DDDDDD', + * padding: 10 + * }, + * countContainer: { + * alignItems: 'center', + * padding: 10 + * }, + * countText: { + * color: '#FF00FF' + * } + * }) + * + * AppRegistry.registerComponent('App', () => App) + * ``` + * + */ + class TouchableOpacity extends React.Component { + state: State = { + anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), + pressability: new Pressability(this._createPressabilityConfig()), + }; + + _createPressabilityConfig(): PressabilityConfig { + return { + cancelable: !this.props.rejectResponderTermination, + disabled: this.props.disabled ?? this.props.accessibilityState?.disabled, + hitSlop: this.props.hitSlop, + delayLongPress: this.props.delayLongPress, + delayPressIn: this.props.delayPressIn, + delayPressOut: this.props.delayPressOut, + minPressDuration: 0, + pressRectOffset: this.props.pressRetentionOffset, + onBlur: event => { + if (Platform.isTV) { + this._opacityInactive(250); + } + if (this.props.onBlur != null) { + this.props.onBlur(event); + } + }, + onFocus: event => { + if (Platform.isTV) { + this._opacityActive(150); + } + if (this.props.onFocus != null) { + this.props.onFocus(event); + } + }, + onLongPress: this.props.onLongPress, + onPress: this.props.onPress, + onPressIn: event => { + this._opacityActive( + event.dispatchConfig.registrationName === 'onResponderGrant' + ? 0 + : 150, + ); + if (this.props.onPressIn != null) { + this.props.onPressIn(event); + } + }, + onPressOut: event => { + this._opacityInactive(250); + if (this.props.onPressOut != null) { + this.props.onPressOut(event); + } + }, + }; + } + + /** + * Animate the touchable to a new opacity. + */ + _setOpacityTo(toValue: number, duration: number): void { + Animated.timing(this.state.anim, { + toValue, + duration, + // $FlowFixMe[method-unbinding] + easing: Easing.inOut(Easing.quad), + useNativeDriver: Platform.OS !== 'android', + }).start(); + } + + _opacityActive(duration: number): void { + this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration); + } + + _opacityInactive(duration: number): void { + this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration); + } + + _getChildStyleOpacityWithDefault(): number { + const opacity = flattenStyle(this.props.style)?.opacity; + return typeof opacity === 'number' ? opacity : 1; + } + + render(): React.Node { + // BACKWARD-COMPATIBILITY: Focus and blur events were never supported before + // adopting `Pressability`, so preserve that behavior. + const { + onBlur, + onFocus, + ...eventHandlersWithoutBlurAndFocus + } = this.state.pressability.getEventHandlers(); + + const accessibilityState = + this.props.disabled != null + ? { + ...this.props.accessibilityState, + disabled: this.props.disabled, + } + : this.props.accessibilityState; + + return ( + + {this.props.children} + {__DEV__ ? ( + + ) : null} + + ); + } + + componentDidUpdate(prevProps: Props, prevState: State) { + this.state.pressability.configure(this._createPressabilityConfig()); + if (this.props.disabled !== prevProps.disabled) { + this._opacityInactive(250); + } + } + + componentWillUnmount(): void { + this.state.pressability.reset(); + } + } + + const Touchable = (React.forwardRef((props, ref) => ( + + )): React.AbstractComponent>); + + Touchable.displayName = 'TouchableOpacity'; + + module.exports = Touchable; + \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/build.gradle b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/build.gradle new file mode 100644 index 0000000000..3bf4a611f9 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/build.gradle @@ -0,0 +1,596 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +plugins { + id("com.android.library") + id("com.facebook.react") + id("maven-publish") + id("de.undercouch.download") +} + +import java.nio.file.Paths + +import de.undercouch.gradle.tasks.download.Download +import org.apache.tools.ant.taskdefs.condition.Os +import org.apache.tools.ant.filters.ReplaceTokens + +def AAR_OUTPUT_URL = "file://${projectDir}/../android" +// We download various C++ open-source dependencies into downloads. +// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk. +// After that we build native code from src/main/jni with module path pointing at third-party-ndk. + +def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR") +def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads") +def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") + +// You need to have following folders in this directory: +// - boost_1_63_0 +// - double-conversion-1.1.6 +// - folly-deprecate-dynamic-initializer +// - glog-0.3.5 +def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") + +// The Boost library is a very large download (>100MB). +// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable +// and the build will use that. +def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") + +// Setup build type for NDK, supported values: {debug, release} +def nativeBuildType = System.getenv("NATIVE_BUILD_TYPE") ?: "release" + +task createNativeDepsDirectories { + downloadsDir.mkdirs() + thirdPartyNdkDir.mkdirs() +} + +task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz")) +} + +task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) { + from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest))) + from("src/main/jni/third-party/boost") + include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp", "asm/**/*.S") + includeEmptyDirs = false + into("$thirdPartyNdkDir/boost") + doLast { + file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}") + } +} + +task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz")) +} + +task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) { + from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest)) + from("src/main/jni/third-party/double-conversion/Android.mk") + include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk") + filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" }) + includeEmptyDirs = false + into("$thirdPartyNdkDir/double-conversion") +} + +task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")) +} + +task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) { + from(dependenciesPath ?: tarTree(downloadFolly.dest)) + from("src/main/jni/third-party/folly/Android.mk") + include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") + eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/folly") +} + +task downloadFmt(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/fmtlib/fmt/archive/${FMT_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "fmt-${FMT_VERSION}.tar.gz")) +} + +task prepareFmt(dependsOn: dependenciesPath ? [] : [downloadFmt], type: Copy) { + from(dependenciesPath ?: tarTree(downloadFmt.dest)) + from("src/main/jni/third-party/fmt/Android.mk") + include("fmt-${FMT_VERSION}/src/**/*", "fmt-${FMT_VERSION}/include/**/*", "Android.mk") + eachFile { fname -> fname.path = (fname.path - "fmt-${FMT_VERSION}/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/fmt") +} + +task downloadLibevent(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}-stable/libevent-${LIBEVENT_VERSION}-stable.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "libevent-${LIBEVENT_VERSION}.tar.gz")) +} + +task prepareLibevent(dependsOn: dependenciesPath ? [] : [downloadLibevent], type: Copy) { + from(dependenciesPath ?: tarTree(downloadLibevent.dest)) + from("src/main/jni/third-party/libevent/Android.mk") + from("src/main/jni/third-party/libevent/event-config.h") + from("src/main/jni/third-party/libevent/evconfig-private.h") + include( + "libevent-${LIBEVENT_VERSION}-stable/*.c", + "libevent-${LIBEVENT_VERSION}-stable/*.h", + "libevent-${LIBEVENT_VERSION}-stable/include/**/*", + "evconfig-private.h", + "event-config.h", + "Android.mk" + ) + eachFile { fname -> fname.path = (fname.path - "libevent-${LIBEVENT_VERSION}-stable/") } + includeEmptyDirs = false + into("$thirdPartyNdkDir/libevent") + + doLast { + ant.move(file: "$thirdPartyNdkDir/libevent/event-config.h", tofile: "$thirdPartyNdkDir/libevent/include/event2/event-config.h") + } +} + +task prepareHermes(dependsOn: createNativeDepsDirectories, type: Copy) { + def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine") + if (!hermesPackagePath) { + throw new GradleScriptException("Could not find the hermes-engine npm package", null) + } + + def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar") + if (!hermesAAR.exists()) { + throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"", null) + } + + def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" }) + + from soFiles + from "src/main/jni/first-party/hermes/Android.mk" + into "$thirdPartyNdkDir/hermes" +} + +task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { + src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") + onlyIfNewer(true) + overwrite(false) + dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz")) +} + +// Prepare glog sources to be compiled, this task will perform steps that normally should've been +// executed by automake. This way we can avoid dependencies on make/automake +task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) { + duplicatesStrategy("warn") + from(dependenciesPath ?: tarTree(downloadGlog.dest)) + from("src/main/jni/third-party/glog/") + include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h") + includeEmptyDirs = false + filesMatching("**/*.h.in") { + filter(ReplaceTokens, tokens: [ + ac_cv_have_unistd_h : "1", + ac_cv_have_stdint_h : "1", + ac_cv_have_systypes_h : "1", + ac_cv_have_inttypes_h : "1", + ac_cv_have_libgflags : "0", + ac_google_start_namespace : "namespace google {", + ac_cv_have_uint16_t : "1", + ac_cv_have_u_int16_t : "1", + ac_cv_have___uint16 : "0", + ac_google_end_namespace : "}", + ac_cv_have___builtin_expect : "1", + ac_google_namespace : "google", + ac_cv___attribute___noinline : "__attribute__ ((noinline))", + ac_cv___attribute___noreturn : "__attribute__ ((noreturn))", + ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))" + ]) + it.path = (it.name - ".in") + } + into("$thirdPartyNdkDir/glog") + + doLast { + copy { + from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files) + includeEmptyDirs = false + into("$thirdPartyNdkDir/glog/exported/glog") + } + } +} + +// Create Android.mk library module based on jsc from npm +task prepareJSC { + doLast { + def jscPackagePath = findNodeModulePath(projectDir, "jsc-android") + if (!jscPackagePath) { + throw new GradleScriptException("Could not find the jsc-android npm package", null) + } + + def jscDist = file("$jscPackagePath/dist") + if (!jscDist.exists()) { + throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory", null) + } + + def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile + def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" }) + + def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" }) + + copy { + from(soFiles) + from(headerFiles) + from("src/main/jni/third-party/jsc/Android.mk") + + filesMatching("**/*.h", { it.path = "JavaScriptCore/${it.name}" }) + + includeEmptyDirs(false) + into("$thirdPartyNdkDir/jsc") + } + } +} +task downloadNdkBuildDependencies { + if (!boostPath) { + dependsOn(downloadBoost) + } + dependsOn(downloadDoubleConversion) + dependsOn(downloadFolly) + dependsOn(downloadGlog) + dependsOn(downloadFmt) + dependsOn(downloadLibevent) +} + +/** + * Finds the path of the installed npm package with the given name using Node's + * module resolution algorithm, which searches "node_modules" directories up to + * the file system root. This handles various cases, including: + * + * - Working in the open-source RN repo: + * Gradle: /path/to/react-native/ReactAndroid + * Node module: /path/to/react-native/node_modules/[package] + * + * - Installing RN as a dependency of an app and searching for hoisted + * dependencies: + * Gradle: /path/to/app/node_modules/react-native/ReactAndroid + * Node module: /path/to/app/node_modules/[package] + * + * - Working in a larger repo (e.g., Facebook) that contains RN: + * Gradle: /path/to/repo/path/to/react-native/ReactAndroid + * Node module: /path/to/repo/node_modules/[package] + * + * The search begins at the given base directory (a File object). The returned + * path is a string. + */ +def findNodeModulePath(baseDir, packageName) { + def basePath = baseDir.toPath().normalize() + // Node's module resolution algorithm searches up to the root directory, + // after which the base path will be null + while (basePath) { + def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName) + if (candidatePath.toFile().exists()) { + return candidatePath.toString() + } + basePath = basePath.getParent() + } + return null +} + +def getNdkBuildName() { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + return "ndk-build.cmd" + } else { + return "ndk-build" + } +} + +def findNdkBuildFullPath() { + // android.ndkDirectory should return project.android.ndkVersion ndkDirectory + def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null + if (ndkDir) { + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + // we allow to provide full path to ndk-build tool + if (hasProperty("ndk.command")) { + return property("ndk.command") + } + // or just a path to the containing directory + if (hasProperty("ndk.path")) { + ndkDir = property("ndk.path") + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + // @TODO ANDROID_NDK && ndk.dir is deprecated and will be removed in the future. + if (System.getenv("ANDROID_NDK") != null) { + ndkDir = System.getenv("ANDROID_NDK") + return new File(ndkDir, getNdkBuildName()).getAbsolutePath() + } + + return null +} + +def reactNativeDevServerPort() { + def value = project.getProperties().get("reactNativeDevServerPort") + return value != null ? value : "8081" +} + +def reactNativeInspectorProxyPort() { + def value = project.getProperties().get("reactNativeInspectorProxyPort") + return value != null ? value : reactNativeDevServerPort() +} + +def reactNativeArchitectures() { + def isDebug = gradle.startParameter.taskRequests.any { + it.args.any { it.endsWith("Debug") } + } + def value = project.getProperties().get("reactNativeDebugArchitectures") + return value != null && isDebug ? value : "all" +} + +def getNdkBuildFullPath() { + def ndkBuildFullPath = findNdkBuildFullPath() + if (ndkBuildFullPath == null) { + throw new GradleScriptException( + "ndk-build binary cannot be found, check if you've set " + + "\$ANDROID_NDK environment variable correctly or if ndk.dir is " + + "setup in local.properties", + null) + } + if (!new File(ndkBuildFullPath).canExecute()) { + throw new GradleScriptException( + "ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" + + "Check that the \$ANDROID_NDK environment variable, or ndk.dir in local.properties, is set correctly.\n" + + "(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)", + null) + } + return ndkBuildFullPath +} + +def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { + dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFmt, prepareFolly, prepareGlog, prepareLibevent, extractAARHeaders, extractJNIFiles) + dependsOn("generateCodegenArtifactsFromSchema"); + + inputs.dir("$projectDir/../ReactCommon") + inputs.dir("src/main/jni") + inputs.dir("src/main/java/com/facebook/react/turbomodule/core/jni") + inputs.dir("src/main/java/com/facebook/react/modules/blob") + outputs.dir("$buildDir/react-ndk/all") + def commandLineArgs = [ + getNdkBuildFullPath(), + "APP_ABI=${reactNativeArchitectures()}", + "NDK_DEBUG=" + (nativeBuildType.equalsIgnoreCase("debug") ? "1" : "0"), + "NDK_PROJECT_PATH=null", + "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", + "NDK_OUT=" + temporaryDir, + "NDK_LIBS_OUT=$buildDir/react-ndk/all", + "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", + "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", + "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", + "-C", file("src/main/jni/react/jni").absolutePath, + "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors() + ] + if (Os.isFamily(Os.FAMILY_MAC)) { + // This flag will suppress "fcntl(): Bad file descriptor" warnings on local builds. + commandLineArgs.add("--output-sync=none") + } + commandLine(commandLineArgs) +} + +def cleanReactNdkLib = tasks.register("cleanReactNdkLib", Exec) { + ignoreExitValue(true) + errorOutput(new ByteArrayOutputStream()) + commandLine(getNdkBuildFullPath(), + "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", + "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", + "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "-C", file("src/main/jni/react/jni").absolutePath, + "clean") + doLast { + file(AAR_OUTPUT_URL).delete() + println("Deleted aar output dir at ${file(AAR_OUTPUT_URL)}") + } +} + +def packageReactNdkLibs = tasks.register("packageReactNdkLibs", Copy) { + dependsOn(buildReactNdkLib) + from("$buildDir/react-ndk/all") + into("$buildDir/react-ndk/exported") + exclude("**/libjsc.so") + exclude("**/libhermes.so") +} + +def packageReactNdkLibsForBuck = tasks.register("packageReactNdkLibsForBuck", Copy) { + dependsOn(packageReactNdkLibs) + from("$buildDir/react-ndk/exported") + into("src/main/jni/prebuilt/lib") +} + +task extractAARHeaders { + doLast { + configurations.extractHeaders.files.each { + def file = it.absoluteFile + def packageName = file.name.tokenize('-')[0] + copy { + from zipTree(file) + into "$projectDir/src/main/jni/first-party/$packageName/headers" + include "**/*.h" + } + } + } +} + +task extractJNIFiles { + doLast { + configurations.extractJNI.files.each { + def file = it.absoluteFile + def packageName = file.name.tokenize('-')[0] + copy { + from zipTree(file) + into "$projectDir/src/main/jni/first-party/$packageName/" + include "jni/**/*" + } + } + } +} + +task installArchives { + dependsOn("publishReleasePublicationToNpmRepository") +} + +android { + compileSdkVersion 30 + ndkVersion ANDROID_NDK_VERSION + if (ANDROID_NDK_PATH != null) { + ndkPath ANDROID_NDK_PATH + } + + defaultConfig { + minSdkVersion(21) + targetSdkVersion(28) + versionCode(1) + versionName("1.0") + + consumerProguardFiles("proguard-rules.pro") + + buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") + buildConfigField("int", "EXOPACKAGE_FLAGS", "0") + buildConfigField("int", "HERMES_BYTECODE_VERSION", "0") + + resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() + resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() + + testApplicationId("com.facebook.react.tests.gradle") + testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner") + } + + sourceSets.main { + jni.srcDirs = [] + jniLibs.srcDir("$buildDir/react-ndk/exported") + res.srcDirs = ["src/main/res/devsupport", "src/main/res/shell", "src/main/res/views/modal", "src/main/res/views/uimanager"] + java { + srcDirs = ["src/main/java", "src/main/libraries/soloader/java", "src/main/jni/first-party/fb/jni/java"] + exclude("com/facebook/react/processing") + exclude("com/facebook/react/module/processing") + } + } + + preBuild.dependsOn(packageReactNdkLibs) + clean.dependsOn(cleanReactNdkLib) + + lintOptions { + abortOnError(false) + } + + packagingOptions { + exclude("META-INF/NOTICE") + exclude("META-INF/LICENSE") + } + + configurations { + extractHeaders + extractJNI + javadocDeps.extendsFrom api + } +} + +dependencies { + api("com.facebook.infer.annotation:infer-annotation:0.18.0") + api("com.facebook.yoga:proguard-annotations:1.19.0") + api("javax.inject:javax.inject:1") + api("androidx.appcompat:appcompat:1.0.2") + api("androidx.autofill:autofill:1.1.0") + api("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + api("com.facebook.fresco:fresco:${FRESCO_VERSION}") + api("com.facebook.fresco:imagepipeline-okhttp3:${FRESCO_OKHTTP_VERSION}") + api("com.facebook.fresco:ui-common:${FRESCO_VERSION}") + api("com.facebook.soloader:soloader:${SO_LOADER_VERSION}") + api("com.google.code.findbugs:jsr305:3.0.2") + api("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}") + api("com.squareup.okhttp3:okhttp-urlconnection:${OKHTTP_VERSION}") + api("com.squareup.okio:okio:2.9.0") + api("com.facebook.fbjni:fbjni-java-only:0.2.2") + extractHeaders("com.facebook.fbjni:fbjni:0.2.2:headers") + extractJNI("com.facebook.fbjni:fbjni:0.2.2") + + javadocDeps("com.squareup:javapoet:1.13.0") + + testImplementation("junit:junit:${JUNIT_VERSION}") + testImplementation("org.powermock:powermock-api-mockito2:${POWERMOCK_VERSION}") + testImplementation("org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}") + testImplementation("org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}") + testImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") + testImplementation("org.easytesting:fest-assert-core:2.0M10") + testImplementation("org.robolectric:robolectric:${ROBOLECTRIC_VERSION}") + + androidTestImplementation(fileTree(dir: "src/main/third-party/java/buck-android-support/", include: ["*.jar"])) + androidTestImplementation("androidx.test:runner:${ANDROIDX_TEST_VERSION}") + androidTestImplementation("androidx.test:rules:${ANDROIDX_TEST_VERSION}") + androidTestImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}") +} + +react { + // TODO: The library name is chosen for parity with Fabric components & iOS + // This should be changed to a more generic name, e.g. `ReactCoreSpec`. + libraryName = "rncore" + jsRootDir = file("../Libraries") + reactRoot = file("$projectDir/..") + useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + // Applies the component for the release build variant. + from components.release + + // You can then customize attributes of the publication as shown below. + artifactId = POM_ARTIFACT_ID + groupId = GROUP + version = VERSION_NAME + + pom { + name = POM_NAME + description = "A framework for building native apps with React" + url = "https://github.com/facebook/react-native" + + developers { + developer { + id = "facebook" + name = "Facebook" + } + } + + licenses { + license { + name = "MIT License" + url = "https://github.com/facebook/react-native/blob/HEAD/LICENSE" + distribution = "repo" + } + } + + scm { + url = "https://github.com/facebook/react-native.git" + connection = "scm:git:https://github.com/facebook/react-native.git" + developerConnection = "scm:git:git@github.com:facebook/react-native.git" + } + } + } + } + + repositories { + maven { + name = "npm" + url = AAR_OUTPUT_URL + } + } + } +} diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/gradle.properties b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/gradle.properties new file mode 100644 index 0000000000..bf7b44da14 --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/ReactAndroid/gradle.properties @@ -0,0 +1,27 @@ +VERSION_NAME=0.67.2 +GROUP=com.facebook.react + +POM_NAME=ReactNative +POM_ARTIFACT_ID=react-native +POM_PACKAGING=aar + +MOCKITO_CORE_VERSION=2.26.0 +POWERMOCK_VERSION=2.0.2 +ROBOLECTRIC_VERSION=4.4 +JUNIT_VERSION=4.12 + +ANDROIDX_TEST_VERSION=1.1.0 +FRESCO_VERSION=2.5.0 +OKHTTP_VERSION=4.9.3 +FRESCO_OKHTTP_VERSION=3.14.9 +SO_LOADER_VERSION=0.10.1 + +BOOST_VERSION=1_63_0 +DOUBLE_CONVERSION_VERSION=1.1.6 +FOLLY_VERSION=2021.06.28.00 +FMT_VERSION=6.2.1 +LIBEVENT_VERSION=2.1.12 +GLOG_VERSION=0.3.5 + +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/scripts/find-node.sh b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/scripts/find-node.sh new file mode 100644 index 0000000000..f508a48c2c --- /dev/null +++ b/packages/rnv/pluginTemplates/react-native/overrides@0.67.2/scripts/find-node.sh @@ -0,0 +1,62 @@ + +# +# NOTE +# This needs to be disabled since it's failing to find node on some indel machines +# + +#!/bin/bash +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +# set -e + +# # remove global prefix if it's already set +# # the running shell process will choose a node binary and a global package directory breaks version managers +# unset PREFIX + +# # Support Homebrew on M1 +# HOMEBREW_M1_BIN=/opt/homebrew/bin +# if [[ -d $HOMEBREW_M1_BIN && ! $PATH =~ $HOMEBREW_M1_BIN ]]; then +# export PATH="$HOMEBREW_M1_BIN:$PATH" +# fi + +# # Define NVM_DIR and source the nvm.sh setup script +# [ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm" + +# # Source nvm with '--no-use' and then `nvm use` to respect .nvmrc +# # See: https://github.com/nvm-sh/nvm/issues/2053 +# if [[ -s "$HOME/.nvm/nvm.sh" ]]; then +# # shellcheck source=/dev/null +# . "$HOME/.nvm/nvm.sh" --no-use +# nvm use 2> /dev/null || nvm use default +# elif [[ -x "$(command -v brew)" && -s "$(brew --prefix nvm)/nvm.sh" ]]; then +# # shellcheck source=/dev/null +# . "$(brew --prefix nvm)/nvm.sh" --no-use +# nvm use 2> /dev/null || nvm use default +# fi + +# # Set up the nodenv node version manager if present +# if [[ -x "$HOME/.nodenv/bin/nodenv" ]]; then +# eval "$("$HOME/.nodenv/bin/nodenv" init -)" +# elif [[ -x "$(command -v brew)" && -x "$(brew --prefix nodenv)/bin/nodenv" ]]; then +# eval "$("$(brew --prefix nodenv)/bin/nodenv" init -)" +# fi + +# # Set up the ndenv of anyenv if preset +# if [[ ! -x node && -d ${HOME}/.anyenv/bin ]]; then +# export PATH=${HOME}/.anyenv/bin:${PATH} +# if [[ "$(anyenv envs | grep -c ndenv )" -eq 1 ]]; then +# eval "$(anyenv init -)" +# fi +# fi + +# # Set up asdf-vm if present +# if [[ -f "$HOME/.asdf/asdf.sh" ]]; then +# # shellcheck source=/dev/null +# . "$HOME/.asdf/asdf.sh" +# elif [[ -x "$(command -v brew)" && -f "$(brew --prefix asdf)/asdf.sh" ]]; then +# # shellcheck source=/dev/null +# . "$(brew --prefix asdf)/asdf.sh" +# fi \ No newline at end of file diff --git a/packages/rnv/pluginTemplates/recyclerlistview/overrides.json b/packages/rnv/pluginTemplates/recyclerlistview/overrides.json new file mode 100644 index 0000000000..737b09caa8 --- /dev/null +++ b/packages/rnv/pluginTemplates/recyclerlistview/overrides.json @@ -0,0 +1,7 @@ +{ + "overrides": { + "DISABLE-dist/reactnative/core/RecyclerListView.js": { + "var debounce_1 = require(\"lodash-es/debounce\");": "import debounce from 'lodash-es/debounce';var debounce_1 = { default: debounce};" + } + } +} diff --git a/packages/rnv/pluginTemplates/renative.plugins.json b/packages/rnv/pluginTemplates/renative.plugins.json index 4828657470..82ae4299f7 100644 --- a/packages/rnv/pluginTemplates/renative.plugins.json +++ b/packages/rnv/pluginTemplates/renative.plugins.json @@ -1,1352 +1,1204 @@ { + "disableRnvDefaultOverrides": true, "pluginTemplates": { - "react-native": { - "version": "0.67.2", + "@bam.tech/react-native-image-resizer": { "android": { - "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", - "path": "react-native" + "package": "com.reactnativeimageresizer.ImageResizerPackage" }, "androidtv": { - "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", - "path": "react-native" + "package": "com.reactnativeimageresizer.ImageResizerPackage" }, "firetv": { - "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", - "path": "react-native" + "package": "com.reactnativeimageresizer.ImageResizerPackage" }, - "androidwear": { - "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", - "path": "react-native" - } - }, - "react-native-tvos": { - "version": "0.66.3-1" + "ios": { + "podName": "react-native-image-resizer" + }, + "macos": { + "podName": "react-native-image-resizer" + }, + "tvos": { + "podName": "react-native-image-resizer" + }, + "version": "3.0.5" }, - "renative": { - "version": "0.36.0-canary.4", + "@flexn/create": { + "androidtv": { + "activityImports": [ + "io.flexn.create.TvRemoteHandlerModule", + "android.view.KeyEvent;" + ], + "activityMethods": [ + "override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"up\");", + "return super.onKeyUp(keyCode, event)", + "}", + "override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {", + " TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"longPress\");", + " return super.onKeyLongPress(keyCode, event)", + "}", + "override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {", + "if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {", + "event?.startTracking();", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event,\"down\");", + "return true;", + "}", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"down\");", + "return super.onKeyDown(keyCode, event)", + "}" + ], + "package": "io.flexn.create.FlexnCreatePackage", + "projectName": "flexn-io-create" + }, + "firetv": { + "activityImports": [ + "io.flexn.create.TvRemoteHandlerModule", + "android.view.KeyEvent;" + ], + "activityMethods": [ + "override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"up\");", + "return super.onKeyUp(keyCode, event)", + "}", + "override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {", + " TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"longPress\");", + " return super.onKeyLongPress(keyCode, event)", + "}", + "override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {", + "if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {", + "event?.startTracking();", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event,\"down\");", + "return true;", + "}", + "TvRemoteHandlerModule.getInstance().onKeyEvent(event, \"down\");", + "return super.onKeyDown(keyCode, event)", + "}" + ], + "package": "io.flexn.create.FlexnCreatePackage", + "projectName": "flexn-io-create" + }, + "tvos": { + "podName": "FlexnCreate" + }, + "version": "0.21.0-alpha.0", "webpack": { - "modulePaths": true, - "moduleAliases": true + "moduleAliases": true, + "modulePaths": true } }, - "@rnv/renative": { - "version": "0.37.0-canary.13", + "@flexn/recyclerlistview": { + "version": "4.2.6", "webpack": { "modulePaths": true, - "moduleAliases": true - } - }, - "@react-native-community/push-notification-ios": { - "version": "1.0.2", - "ios": { - "podName": "RNCPushNotificationIOS", - "path": "node_modules/@react-native-community/push-notification-ios", - "appDelegateImports": [ - "RNCPushNotificationIOS" - ], - "appDelegateMethods": { - "application": { - "didReceiveRemoteNotification": [ - "RNCPushNotificationIOS.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)" - ], - "didFailToRegisterForRemoteNotificationsWithError": [ - "RNCPushNotificationIOS.didFailToRegisterForRemoteNotificationsWithError(error)" - ], - "didReceive": [ - "RNCPushNotificationIOS.didReceive(notification)" - ], - "didRegister": [ - "RNCPushNotificationIOS.didRegister(notificationSettings)" - ], - "didRegisterForRemoteNotificationsWithDeviceToken": [ - "RNCPushNotificationIOS.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)" - ] - }, - "userNotificationCenter": { - "willPresent": [ - "completionHandler([.alert, .badge, .sound])" - ] - } - } - } - }, - "RCTPushNotification": { - "no-npm": true, - "deprecated": "RCTPushNotification is DEPRECATED. use @react-native-community/push-notification-ios instead", - "ios": { - "appDelegateMethods": { - "application": { - "didReceiveRemoteNotification": [ - "RCTPushNotificationManager.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)" - ], - "didFailToRegisterForRemoteNotificationsWithError": [ - "RCTPushNotificationManager.didFailToRegisterForRemoteNotificationsWithError(error)" - ], - "didReceive": [ - "RCTPushNotificationManager.didReceive(notification)" - ], - "didRegister": [ - "RCTPushNotificationManager.didRegister(notificationSettings)" - ], - "didRegisterForRemoteNotificationsWithDeviceToken": [ - "RCTPushNotificationManager.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)" - ] - }, - "userNotificationCenter": { - "willPresent": [ - "completionHandler([.alert, .badge, .sound])" - ] - } - } - } - }, - "RCTLinkingIOS": { - "no-npm": true, - "ios": { - "appDelegateMethods": { - "application": { - "open": [ - "RCTLinkingManager.application(app, open: url, options: options)" - ] - } - } + "moduleAliases": true, + "nextTranspileModules": ["@flexn/recyclerlistview"] } }, - "react-native-gesture-handler": { - "version": "1.10.3", - "ios": { - "podName": "RNGestureHandler" + "@flexn/sdk": { + "androidtv": { + "package": "io.flexn.sdk.FlexnSdkPackage", + "projectName": "flexn-io-sdk" }, - "macos": { - "podName": "RNGestureHandler" + "firetv": { + "package": "io.flexn.sdk.FlexnSdkPackage", + "projectName": "flexn-io-sdk" }, "tvos": { - "podName": "RNGestureHandler" + "podName": "FlexnSDK" }, + "version": "0.20.0-alpha.25", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "@flexn/shopify-flash-list": { "android": { - "path": "node_modules/react-native-gesture-handler/android", - "package": "com.swmansion.gesturehandler.react.RNGestureHandlerPackage", - "mainActivity": { - "imports": [ - "com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView" - ], - "methods": [ - " override fun createReactActivityDelegate(): ReactActivityDelegate {", - "return object : ReactActivityDelegate(this, mainComponentName) {", - "override fun createRootView():ReactRootView {", - " return RNGestureHandlerEnabledRootView(this@MainActivity)", - " } \n } \n}" - ] - } + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, "androidtv": { - "path": "node_modules/react-native-gesture-handler/android", - "package": "com.swmansion.gesturehandler.react.RNGestureHandlerPackage" + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, "firetv": { - "path": "node_modules/react-native-gesture-handler/android", - "package": "com.swmansion.gesturehandler.react.RNGestureHandlerPackage" - }, - "androidwear": { - "path": "node_modules/react-native-gesture-handler/android", - "package": "com.swmansion.gesturehandler.react.RNGestureHandlerPackage" + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, - "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "react-native-video": { - "version": "5.0.2", "ios": { - "podName": "react-native-video" + "podName": "RNFlashList" }, "tvos": { - "podName": "react-native-video" + "podName": "RNFlashList" + }, + "macos": { + "podName": "RNFlashList" + }, + "webpack": { + "modulePaths": true, + "moduleAliases": true, + "nextTranspileModules": ["@flexn/shopify-flash-list"] }, + "version": "1.4.8" + }, + "@flexn/typescript": { + "version": "0.2.4" + }, + "@lightningjs/cli": { + "version": "2.11.0" + }, + "@lightningjs/sdk": { + "version": "5.2.0" + }, + "@lightningjs/core": { + "version": "2.8.0" + }, + "@lightningjs/ui": { + "version": "1.3.1" + }, + "@lightningjs/ui-components": { + "version": "1.3.1" + }, + "@mapbox/react-native-mapbox-gl": { "android": { - "package": "com.brentvatne.react.ReactVideoPackage" + "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }", + "package": "com.mapbox.rctmgl.RCTMGLPackage", + "path": "{{PLUGIN_ROOT}}/android/rctmgl", + "projectName": "mapbox-react-native-mapbox-gl" }, "androidtv": { - "package": "com.brentvatne.react.ReactVideoPackage" + "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }", + "package": "com.mapbox.rctmgl.RCTMGLPackage", + "path": "{{PLUGIN_ROOT}}/android/rctmgl", + "projectName": "mapbox-react-native-mapbox-gl" }, "firetv": { - "package": "com.brentvatne.react.ReactVideoPackage" + "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }", + "package": "com.mapbox.rctmgl.RCTMGLPackage", + "path": "{{PLUGIN_ROOT}}/android/rctmgl", + "projectName": "mapbox-react-native-mapbox-gl" + }, + "ios": { + "podName": "react-native-mapbox-gl" + }, + "tvos": { + "podName": "react-native-mapbox-gl" }, + "version": "github:nitaliano/react-native-mapbox-gl" + }, + "@miblanchard/react-native-slider": { + "version": "2.1.0", "webpack": { + "moduleAliases": true, "modulePaths": true } }, - "react-native-languages": { - "version": "3.0.2", - "deprecated": "react-native-languages has been DEPRECATED. use react-native-localize instead", + "@monterosa/react-native-parallax-scroll": { + "version": "1.8.0", + "webpack": { + "moduleAliases": { + "@monterosa/react-native-parallax-scroll": { + "projectPath": "{{PLUGIN_ROOT}}" + } + }, + "modulePaths": true + } + }, + "@noriginmedia/react-spatial-navigation": { + "version": "2.12.9" + }, + "@notifee/react-native": { + "android": { + "BuildGradle": { + "allprojects": { + "repositories": { + " maven { url \"{{resolvePackage(@notifee/react-native)}}/android/libs\" }": true + } + } + }, + "package": "io.invertase.notifee.NotifeePackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "@notifee-react-native" + }, "ios": { - "podName": "RNLanguages" + "path": "{{PLUGIN_ROOT}}", + "podName": "RNNotifee" }, + "version": "5.3.0" + }, + "@reach/router": "1.3.4", + "@react-native-async-storage/async-storage": { "android": { - "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" }, "androidtv": { - "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" }, "firetv": { - "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" + }, + "ios": { + "podName": "RNCAsyncStorage" + }, + "macos": { + "podName": "RNCAsyncStorage" + }, + "tvos": { + "podName": "RNCAsyncStorage" + }, + "version": "1.17.10", + "webpack": { + "modulePaths": true } }, - "react-native-localize": { - "version": "1.4.0", + "@react-native-camera-roll/camera-roll": { + "android": { + "package": "com.reactnativecommunity.cameraroll.CameraRollPackage" + }, "ios": { - "podName": "RNLocalize" + "podName": "react-native-cameraroll" }, - "tvos": { - "podName": "RNLocalize" + "version": "5.2.4" + }, + "@react-native-clipboard/clipboard": { + "android": { + "package": "com.reactnativecommunity.clipboard.ClipboardPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "ios": { + "podName": "RNCClipboard" + }, + "macos": { + "podName": "RNCClipboard" }, + "version": "1.9.0" + }, + "@react-native-community/async-storage": { "android": { - "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" }, "androidtv": { - "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" }, "firetv": { - "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" - } - }, - "react-native-linear-gradient": { - "version": "2.4.0", + "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage", + "projectName": "react-native-community-async-storage" + }, "ios": { - "podName": "BVLinearGradient" + "podName": "RNCAsyncStorage" }, "macos": { - "podName": "BVLinearGradient" + "podName": "RNCAsyncStorage" }, "tvos": { - "podName": "BVLinearGradient" + "podName": "RNCAsyncStorage" }, + "version": "1.12.1", + "webpack": { + "modulePaths": true + } + }, + "@react-native-community/blur": { "android": { - "package": "com.BV.LinearGradient.LinearGradientPackage" + "package": "com.cmcewen.blurview.BlurViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-blur" }, "androidtv": { - "package": "com.BV.LinearGradient.LinearGradientPackage" + "package": "com.cmcewen.blurview.BlurViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-blur" }, "firetv": { - "package": "com.BV.LinearGradient.LinearGradientPackage" + "package": "com.cmcewen.blurview.BlurViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-blur" }, - "androidwear": { - "package": "com.BV.LinearGradient.LinearGradientPackage" + "ios": { + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-blur" }, - "webpack": { - "modulePaths": [ - "react-native-web-linear-gradient", - "react-native-linear-gradient" - ], - "moduleAliases": { - "react-native-linear-gradient": "react-native-web-linear-gradient" - } + "tvos": { + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-blur" }, - "npm": { - "react-native-web-linear-gradient": "1.0.5" - } + "version": "4.3.0", + "web": null }, - "react-native-fetch-blob": { - "deprecated": "react-native-fetch-blob plugin is DEPRECATED. use rn-fetch-blob instead", - "version": "0.10.8", - "ios": { - "podName": "react-native-fetch-blob" - }, - "tvos": { - "podName": "react-native-fetch-blob" + "@react-native-community/cameraroll": { + "android": { + "package": "com.reactnativecommunity.cameraroll.CameraRollPackage" }, - "macos": { - "podName": "react-native-fetch-blob" + "ios": { + "podName": "react-native-cameraroll" }, + "version": "1.2.1" + }, + "@react-native-community/checkbox": { "android": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" + "package": "com.reactnativecommunity.checkbox.ReactCheckBoxPackage" }, "androidtv": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" + "package": "com.reactnativecommunity.checkbox.ReactCheckBoxPackage" }, "firetv": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" - } + "package": "com.reactnativecommunity.checkbox.ReactCheckBoxPackage" + }, + "ios": { + "podName": "RNCCheckbox" + }, + "macos": { + "podName": "RNCCheckbox" + }, + "tvos": { + "podName": "RNCCheckbox" + }, + "version": "0.5.9" }, - "rn-fetch-blob": { - "version": "0.11.2", + "@react-native-community/cli": { + "version": "^6.0.0" + }, + "@react-native-community/cli-platform-android": { + "version": "^6.0.0" + }, + "@react-native-community/cli-platform-ios": { + "version": "^6.0.0" + }, + "@react-native-community/clipboard": { + "android": { + "package": "com.reactnativecommunity.clipboard.ClipboardPackage" + }, "ios": { - "podName": "rn-fetch-blob" + "podName": "RNCClipboard" + }, + "macos": { + "podName": "RNCClipboard" }, + "version": "1.5.1" + }, + "@react-native-community/datetimepicker": { "android": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" + "package": "com.reactcommunity.rndatetimepicker.RNDateTimePickerPackage" }, - "androidtv": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" + "ios": { + "podName": "RNDateTimePicker" }, - "firetv": { - "package": "com.RNFetchBlob.RNFetchBlobPackage" + "macos": { + "podName": "RNDateTimePicker" }, - "webpack": { - "modulePaths": true - } + "version": "6.7.5" }, - "react-native-camera": { - "version": "3.6.0", + "@react-native-community/geolocation": { + "android": { + "package": "com.reactnativecommunity.geolocation.GeolocationPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, "ios": { - "podName": "react-native-camera" + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-geolocation" }, + "version": "2.0.2" + }, + "@react-native-community/masked-view": { "android": { - "package": "org.reactnative.camera.RNCameraPackage", - "app/build.gradle": { - "defaultConfig": [ - "missingDimensionStrategy 'react-native-camera', 'general'" - ] - } + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-masked-view" }, "androidtv": { - "package": "org.reactnative.camera.RNCameraPackage", - "app/build.gradle": { - "defaultConfig": [ - "missingDimensionStrategy 'react-native-camera', 'general'" - ] - } + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-masked-view" }, "firetv": { - "package": "org.reactnative.camera.RNCameraPackage", - "app/build.gradle": { - "defaultConfig": [ - "missingDimensionStrategy 'react-native-camera', 'general'" - ] - } - }, - "webpack": { - "modulePaths": true - } - }, - "@react-native-community/viewpager": { - "version": "2.0.2", - "ios": { - "podName": "react-native-viewpager", - "path": "node_modules/@react-native-community/viewpager" + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "react-native-community-masked-view" }, - "android": { - "path": "node_modules/@react-native-community/viewpager/android", - "package": "com.reactnativecommunity.viewpager.RNCViewPagerPackage" - } - }, - "react-native-fs": { - "version": "2.18.0", "ios": { - "podName": "RNFS" + "path": "{{PLUGIN_ROOT}}", + "podName": "RNCMaskedView" }, "tvos": { - "podName": "RNFS" - }, - "macos": { - "podName": "RNFS" + "path": "{{PLUGIN_ROOT}}", + "podName": "RNCMaskedView" }, + "version": "0.1.6" + }, + "@react-native-community/netinfo": { "android": { - "package": "com.rnfs.RNFSPackage" + "package": "com.reactnativecommunity.netinfo.NetInfoPackage", + "projectName": "react-native-community-netinfo" }, "androidtv": { - "package": "com.rnfs.RNFSPackage" + "package": "com.reactnativecommunity.netinfo.NetInfoPackage" + }, + "androidwear": { + "package": "com.reactnativecommunity.netinfo.NetInfoPackage" }, "firetv": { - "package": "com.rnfs.RNFSPackage" - } - }, - "react-native-idle-timer": { - "version": "2.1.5", - "ios": { - "path": "node_modules/react-native-idle-timer", - "podName": "react-native-idle-timer" + "package": "com.reactnativecommunity.netinfo.NetInfoPackage" }, - "android": { - "package": "com.marcshilling.idletimer.IdleTimerPackage" + "ios": { + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-netinfo" }, - "androidtv": { - "package": "com.marcshilling.idletimer.IdleTimerPackage" + "tvos": { + "podName": "react-native-netinfo" }, - "firetv": { - "package": "com.marcshilling.idletimer.IdleTimerPackage" - } - }, - "react-native-image-cache-manager": { - "version": "1.0.1" - }, - "react-native-dialog": { - "version": "5.6.0", + "version": "9.3.8", "webpack": { + "moduleAliases": { + "@react-native-community/netinfo": { + "projectPath": "{{PLUGIN_ROOT}}/web" + } + }, "modulePaths": true } }, - "@monterosa/react-native-parallax-scroll": { - "version": "1.8.0", - "webpack": { - "modulePaths": [ - "node_modules/@monterosa/react-native-parallax-scroll" + "@react-native-community/push-notification-ios": { + "ios": { + "appDelegateImports": [ + "RNCPushNotificationIOS" ], - "moduleAliases": { - "@monterosa/react-native-parallax-scroll": { - "projectPath": "node_modules/@monterosa/react-native-parallax-scroll" + "appDelegateMethods": { + "application": { + "didFailToRegisterForRemoteNotificationsWithError": [ + "RNCPushNotificationIOS.didFailToRegisterForRemoteNotificationsWithError(error)" + ], + "didReceive": null, + "didReceive": [ + "RNCPushNotificationIOS.didReceive(notification)" + ], + "didReceiveRemoteNotification": [ + "RNCPushNotificationIOS.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)" + ], + "didRegister": [ + "RNCPushNotificationIOS.didRegister(notificationSettings)" + ], + "didRegisterForRemoteNotificationsWithDeviceToken": [ + "RNCPushNotificationIOS.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)" + ] + }, + "userNotificationCenter": { + "willPresent": [ + "completionHandler([.alert, .badge, .sound])" + ] } - } - } + }, + "path": "{{PLUGIN_ROOT}}", + "podName": "RNCPushNotificationIOS" + }, + "version": "1.11.0" }, - "react-native-dominant-color": { - "version": "1.0.0", + "@react-native-community/slider": { "android": { - "path": "node_modules/react-native-dominant-color/android", - "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage" - }, - "androidtv": { - "path": "node_modules/react-native-dominant-color/android", - "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage" + "package": "com.reactnativecommunity.slider.ReactSliderPackage" }, - "firetv": { - "path": "node_modules/react-native-dominant-color/android", - "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage" - } - }, - "react-native-extract-color": { - "version": "github:kasinskas/react-native-extract-color", "ios": { - "podName": "RNExtractColor", - "path": "node_modules/react-native-extract-color/ios" + "podName": "react-native-slider" }, - "tvos": { - "podName": "RNExtractColor", - "path": "node_modules/react-native-extract-color/ios" - } + "macos": { + "podName": "react-native-slider" + }, + "version": "4.4.2" }, - "react-native-uri-scheme": { - "version": "1.0.16", + "@react-native-community/viewpager": { "android": { - "package": "com.rs.RNUriScheme.RNUriSchemePackage" + "package": "com.reactnativecommunity.viewpager.RNCViewPagerPackage", + "path": "{{PLUGIN_ROOT}}/android" }, "ios": { - "podName": "RNUriScheme" - } - }, - "react-native-biometrics": { - "version": "1.6.1", - "ios": { - "podName": "react-native-biometrics" + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-viewpager" }, - "android": { - "projectName": "react-native-biometrics", - "package": "com.rnbiometrics.ReactNativeBiometricsPackage" - } + "version": "5.0.11" }, - "react-native-fbsdk": { - "props": { - "APP_ID": "", - "APP_NAME": "", - "URL_SCHEMES": null, - "QUERIES_SCHEMES": null + "@react-native-firebase/analytics": { + "android": { + "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", + "projectName": "@react-native-firebase_analytics" + }, + "androidtv": { + "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", + "projectName": "@react-native-firebase_analytics" + }, + "firetv": { + "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", + "projectName": "@react-native-firebase_analytics" }, - "version": "1.0.4", "ios": { - "podName": "react-native-fbsdk", "appDelegateImports": [ - "FBSDKCoreKit" + "Firebase" ], "appDelegateMethods": { "application": { "didFinishLaunchingWithOptions": [ - "ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)" - ], - "open": [ - "ApplicationDelegate.shared.application(app, open: url, options: options)" + { + "order": -1, + "value": "FirebaseApp.configure()", + "weight": 1 + } ] } }, - "plist": { - "FacebookAppID": "{{props.APP_ID}}", - "FacebookDisplayName": "{{props.APP_NAME}}", - "CFBundleURLTypes": [ - { - "CFBundleTypeRole": "Editor", - "CFBundleURLSchemes": [ - "fb{{props.APP_ID}}" - ] - } - ], - "LSApplicationQueriesSchemes": [ - "fbapi", - "fb-messenger-share-api", - "fbauth2", - "fbshareextension" + "isStatic": true, + "podName": "RNFBAnalytics", + "xcodeproj": { + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" ] } }, + "pluginDependencies": { + "@react-native-firebase/app": "source:rnv" + }, + "version": "16.7.0" + }, + "@react-native-firebase/app": { "android": { - "package": "com.facebook.reactnative.androidsdk.FBSDKPackage", - "implementations": [ - "'com.facebook.android:facebook-android-sdk:[4,5)'" - ], - "imports": [ - "com.facebook.CallbackManager" - ], - "mainApplication": { - "methods": [ - " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" - ] - }, - "mainActivity": { - "imports": [], - "createMethods": [], - "resultMethods": [ - "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" - ] - }, "AndroidManifest": { "children": [ { - "tag": "application", "android:name": ".MainApplication", "children": [ { - "tag": "meta-data", - "android:name": "com.facebook.sdk.ApplicationId", - "android:value": "@string/facebook_app_id" + "android:name": "com.google.firebase.messaging.default_notification_icon", + "android:resource": "@drawable/ic_stat_ic_notification", + "tag": "meta-data" } - ] + ], + "tag": "application" } ] }, - "ResourceStrings": { - "children": [ - { - "tag": "string", - "name": "facebook_app_id", - "child_value": "{{props.APP_ID}}" - } + "app/build.gradle": { + "apply": [ + "plugin: 'com.google.gms.google-services'" ] - } + }, + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'com.google.gms:google-services:4.3.14'" + ] + } + }, + "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", + "projectName": "@react-native-firebase_app" }, "androidtv": { - "package": "com.facebook.reactnative.androidsdk.FBSDKPackage", - "implementations": [ - "'com.facebook.android:facebook-android-sdk:[4,5)'" - ], - "imports": [ - "com.facebook.CallbackManager" - ], - "mainApplication": { - "methods": [ - " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" + "app/build.gradle": { + "apply": [ + "plugin: 'com.google.gms.google-services'" ] }, - "mainActivity": { - "imports": [], - "createMethods": [], - "resultMethods": [ - "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" - ] - } + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'com.google.gms:google-services:4.2.0'" + ] + } + }, + "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", + "projectName": "@react-native-firebase_app" }, "firetv": { - "package": "com.facebook.reactnative.androidsdk.FBSDKPackage", - "implementations": [ - "'com.facebook.android:facebook-android-sdk:[4,5)'" - ], - "imports": [ - "com.facebook.CallbackManager" - ], - "mainApplication": { - "methods": [ - " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" + "app/build.gradle": { + "apply": [ + "plugin: 'com.google.gms.google-services'" ] }, - "mainActivity": { - "imports": [], - "createMethods": [], - "resultMethods": [ - "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'com.google.gms:google-services:4.2.0'" + ] + } + }, + "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", + "projectName": "@react-native-firebase_app" + }, + "ios": { + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "FirebaseApp.configure()" + ] + } + }, + "isStatic": true, + "podName": "RNFBApp", + "xcodeproj": { + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" ] } - } - }, - "react-native-zip-archive": { - "version": "4.0.2", - "ios": { - "podName": "RNZipArchive" }, + "version": "16.7.0" + }, + "@react-native-firebase/auth": { "android": { - "package": "com.rnziparchive.RNZipArchivePackage" - }, - "androidtv": { - "package": "com.rnziparchive.RNZipArchivePackage" + "package": "io.invertase.firebase.auth.ReactNativeFirebaseAuthPackage", + "projectName": "@react-native-firebase_auth" }, - "firetv": { - "package": "com.rnziparchive.RNZipArchivePackage" - } - }, - "react-native-image-picker": { - "version": "0.27.1", "ios": { - "podName": "react-native-image-picker" - }, - "android": { - "package": "com.imagepicker.ImagePickerPackage", - "build.gradle": { - "subprojects": "if (project.name.contains('react-native-image-picker') || project.name.contains('react-native-fetch-blob') || project.name.contains('react-native-linear-gradient') || project.name.contains('react-native-prompt-android')) {\n buildscript {\n repositories {\n jcenter()\n google()\n maven {\n url 'https://dl.bintray.com/android/android-tools/' }\n }\n }\n }" + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + { + "order": -1, + "value": "FirebaseApp.configure()", + "weight": 1 + } + ] + } + }, + "isStatic": true, + "podName": "RNFBAuth", + "xcodeproj": { + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" + ] } }, - "androidtv": { - "package": "com.imagepicker.ImagePickerPackage" + "pluginDependencies": { + "@react-native-firebase/app": "source:rnv" }, - "firetv": { - "package": "com.imagepicker.ImagePickerPackage" - } + "version": "16.7.0" }, - "react-native-nfc-manager": { - "version": "1.2.2", - "ios": { - "podName": "react-native-nfc-manager", - "path": "node_modules/react-native-nfc-manager" - }, + "@react-native-firebase/crashlytics": { "android": { - "package": "community.revteltech.nfc.NfcManagerPackage" + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "BuildGradle": { + "buildscript": { + "dependencies": { + "classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'": true + } + } + }, + "implementations": [ + "'com.google.android.material:material:1.2.1'" + ], + "package": "io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsPackage", + "projectName": "@react-native-firebase_crashlytics" }, "androidtv": { - "package": "community.revteltech.nfc.NfcManagerPackage" + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "BuildGradle": { + "allprojects": { + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + }, + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.28.1'": true + }, + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + } + }, + "package": "io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsPackage", + "projectName": "@react-native-firebase_crashlytics" }, - "firetv": { - "package": "community.revteltech.nfc.NfcManagerPackage" - } - }, - "react-native-fabric": { - "version": "", "ios": { - "podName": "ReactNativeFabric" + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + { + "order": -1, + "value": "FirebaseApp.configure()", + "weight": 1 + } + ] + } + }, + "isStatic": true, + "podName": "RNFBCrashlytics", + "xcodeproj": { + "buildPhases": [ + { + "shellPath": "/bin/sh", + "shellScript": "\"${PODS_ROOT}/Fabric/run\"" + } + ], + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" + ] + } }, - "tvos": { - "podName": "ReactNativeFabric" - } - }, - "react-native-exception-handler": { - "version": "2.6.0", - "ios": { - "podName": "ReactNativeExceptionHandler", - "path": "node_modules/react-native-exception-handler/ios" + "pluginDependencies": { + "@react-native-firebase/analytics": "source:rnv" }, + "version": "16.7.0" + }, + "@react-native-firebase/messaging": { "android": { - "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage" + "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", + "projectName": "@react-native-firebase_messaging" }, "androidtv": { - "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage" + "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", + "projectName": "@react-native-firebase_messaging" }, + "dependsOn": [ + "@react-native-firebase/app" + ], "firetv": { - "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage" - } - }, - "react-native-pdf": { - "version": "5.0.6", + "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", + "projectName": "@react-native-firebase_messaging" + }, "ios": { - "podName": "react-native-pdf" + "isStatic": true, + "podName": "RNFBMessaging" }, - "macos": { - "podName": "react-native-pdf" + "pluginDependencies": { + "@react-native-firebase/app": "source:rnv" }, + "version": "16.7.0" + }, + "@react-native-firebase/storage": { "android": { - "package": "org.wonday.pdf.RCTPdfView" - }, - "androidtv": { - "package": "org.wonday.pdf.RCTPdfView" + "package": "io.invertase.firebase.storage.ReactNativeFirebaseStoragePackage", + "projectName": "@react-native-firebase_storage" }, - "firetv": { - "package": "org.wonday.pdf.RCTPdfView" - } - }, - "react-native-sensors": { - "version": "5.3.3", "ios": { - "podName": "RNSensors" + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + { + "order": -1, + "value": "FirebaseApp.configure()", + "weight": 1 + } + ] + } + }, + "isStatic": true, + "podName": "RNFBStorage", + "xcodeproj": { + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" + ] + } + }, + "pluginDependencies": { + "@react-native-firebase/app": "source:rnv" }, + "version": "16.7.0" + }, + "@react-native-masked-view/masked-view": { "android": { - "package": "com.sensors.RNSensorsPackage" + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "projectName": "react-native-masked-view-masked-view" }, "androidtv": { - "package": "com.sensors.RNSensorsPackage" + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "projectName": "react-native-masked-view-masked-view" }, "firetv": { - "package": "com.sensors.RNSensorsPackage" - } - }, - "react-native-markdown-renderer": { - "version": "3.2.8", - "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "react-native-local-mongodb": { - "version": "2.2.4" - }, - "react-native-actionsheet": { - "version": "2.3.0" - }, - "react-native-animatable": { - "version": "1.3.3", - "webpack": { - "nextTranspileModules": [ - "react-native-animatable" - ] - } - }, - "react-native-autocomplete-input": { - "version": "4.1.0", - "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "react-native-datepicker": { - "version": "1.7.2" - }, - "react-native-extended-stylesheet": { - "version": "0.8.0" - }, - "react-native-keyboard-spacer": { - "version": "0.4.1" - }, - "react-native-material-dropdown": { - "version": "0.11.1" - }, - "react-native-modal": { - "version": "7.0.0", - "webpack": { - "modulePaths": true - } - }, - "react-native-onfido-sdk": { - "version": "0.0.7" - }, - "react-native-pdf-view": { - "version": "0.3.2" - }, - "react-native-progress": { - "version": "3.5.0" - }, - "react-native-root-toast": { - "version": "3.2.1", - "webpack": { - "nextTranspileModules": [ - "react-native-root-siblings", - "static-container" - ] - } - }, - "react-native-viewpager": { - "version": "0.2.13" - }, - "react-native-simple-radio-button": { - "version": "2.7.3" - }, - "react-native-svg-charts": { - "version": "5.3.0" - }, - "react-native-swipeout": { - "version": "2.3.3" - }, - "react-native-fingerprint-scanner": { - "version": "3.0.0", + "package": "org.reactnative.maskedview.RNCMaskedViewPackage", + "projectName": "react-native-masked-view-masked-view" + }, "ios": { - "podName": "react-native-fingerprint-scanner" + "podName": "RNCMaskedView" }, - "android": { - "projectName": "react-native-fingerprint-scanner", - "package": "com.hieuvp.fingerprint.ReactNativeFingerprintScannerPackage" - } - }, - "@react-native-community/geolocation": { - "version": "2.0.2", - "android": { - "path": "/node_modules/@react-native-community/geolocation/android", - "package": "com.reactnativecommunity.geolocation.GeolocationPackage" + "tvos": { + "podName": "RNCMaskedView" }, - "ios": { - "podName": "react-native-geolocation", - "path": "node_modules/@react-native-community/geolocation" - } + "version": "0.2.9" }, - "react-native-keep-awake": { - "version": "4.0.0", - "ios": { - "podName": "react-native-keep-awake" - }, + "@react-native-picker/picker": { "android": { - "package": "com.corbt.keepawake.KCKeepAwakePackage" + "package": "com.reactnativecommunity.picker.RNCPickerPackage" }, "androidtv": { - "package": "com.corbt.keepawake.KCKeepAwakePackage" + "package": "com.reactnativecommunity.picker.RNCPickerPackage" }, "firetv": { - "package": "com.corbt.keepawake.KCKeepAwakePackage" + "package": "com.reactnativecommunity.picker.RNCPickerPackage" }, - "webpack": { - "modulePaths": true - } - }, - "react-native-maps": { - "version": "0.19.0", "ios": { - "podName": "react-native-maps" - } - }, - "react-native-document-picker": { - "version": "3.2.4", - "ios": { - "podName": "react-native-document-picker" + "podName": "RNCPicker" }, - "android": { - "package": "io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage" + "macos": { + "podName": "RNCPicker" }, - "androidtv": { - "package": "io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage" + "tvos": { + "podName": "RNCPicker" }, - "firetv": { - "package": "io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage" - } + "version": "2.4.9" }, - "react-native-image-crop-picker": { - "version": "0.25.3", - "ios": { - "podName": "RNImageCropPicker" + "@react-native-windows/cli": { + "version": "0.67.1" + }, + "@react-navigation": { + "npm": { + "@react-native-community/masked-view": "0.1.11", + "@react-navigation/bottom-tabs": "5.1.1", + "@react-navigation/core": "6.1.0", + "@react-navigation/drawer": "6.1.8", + "@react-navigation/material-bottom-tabs": "6.0.9", + "@react-navigation/material-top-tabs": "5.3.10", + "@react-navigation/native": "6.0.6", + "@react-navigation/native-stack": "5.0.5", + "@react-navigation/routers": "5.1.0", + "@react-navigation/stack": "6.0.11", + "@react-navigation/web": "1.0.0-alpha.9", + "react-native-safe-area-context": "4.3.4" }, + "pluginDependencies": { + "react-native-safe-area-context": "source:rnv", + "react-native-screens": "source:rnv" + } + }, + "@react-navigation/bottom-tabs": "5.1.1", + "@react-navigation/core": "6.1.0", + "@react-navigation/drawer": "6.1.8", + "@react-navigation/material-bottom-tabs": "6.0.9", + "@react-navigation/material-top-tabs": "5.3.10", + "@react-navigation/native": "6.0.6", + "@react-navigation/native-stack": "5.0.5", + "@react-navigation/routers": "5.1.0", + "@react-navigation/stack": "6.0.11", + "@react-navigation/web": "1.0.0-alpha.9", + "@react-navigation/elements": "1.3.17", + "@reduxjs/toolkit": { + "version": "^1.8.1" + }, + "@rnv/renative": { + "version": "0.37.0-canary.7", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "@sentry/react": { + "version": "6.13.3" + }, + "@sentry/react-native": { "android": { - "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" + "app/build.gradle": { + "apply": [ + "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" + ] + }, + "implementation": "implementation project(':@sentry_react-native')", + "package": "io.sentry.react.RNSentryPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "@sentry_react-native" }, "androidtv": { - "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" + "app/build.gradle": { + "apply": [ + "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" + ] + }, + "implementation": "implementation project(':@sentry_react-native')", + "package": "io.sentry.react.RNSentryPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "@sentry_react-native" }, "firetv": { - "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" - } - }, - "react-native-svg": { - "version": "9.4.0", + "app/build.gradle": { + "apply": [ + "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" + ] + }, + "implementation": "implementation project(':@sentry_react-native')", + "package": "io.sentry.react.RNSentryPackage", + "path": "{{PLUGIN_ROOT}}/android", + "projectName": "@sentry_react-native" + }, "ios": { - "podName": "RNSVG" + "podName": "RNSentry", + "xcodeproj": { + "buildPhases": [ + { + "shellPath": "/bin/sh", + "shellScript": "export NODE_BINARY=node\nexport SENTRY_PROPERTIES=../sentry.properties" + } + ] + } }, "macos": { - "podName": "RNSVG" - }, - "tvos": { - "podName": "RNSVG" + "podName": "RNSentry" }, + "version": "3.1.1" + }, + "@sentry/tracing": { + "version": "6.13.3" + }, + "@shopify/flash-list": { "android": { - "package": "com.horcrux.svg.SvgPackage" + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, "androidtv": { - "package": "com.horcrux.svg.SvgPackage" + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, "firetv": { - "package": "com.horcrux.svg.SvgPackage" - }, - "androidwear": { - "package": "com.horcrux.svg.SvgPackage" - }, - "webpack": { - "moduleAliases": { - "react-native-svg": "svgs" - } + "package": "com.shopify.reactnative.flash_list.ReactNativeFlashListPackage" }, - "pluginDependencies": { - "svgs": "source:rnv" - } - }, - "react-native-snap-scrollview": { - "version": "github:reactseals/react-native-snap-scrollview#master", "ios": { - "podName": "react-native-snap-scrollview" + "podName": "RNFlashList" }, "tvos": { - "podName": "react-native-snap-scrollview" + "podName": "RNFlashList" }, - "androidtv": { - "package": "com.reactlibrary.SnapScrollviewPackage" + "macos": { + "podName": "RNFlashList" }, - "firetv": { - "package": "com.reactlibrary.SnapScrollviewPackage" + "webpack": { + "modulePaths": true, + "moduleAliases": true }, - "android": { - "package": "com.reactlibrary.SnapScrollviewPackage" - } + "version": "1.4.1" }, - "react-native-parallax-view": { - "version": "github:reactseals/react-native-parallax-view#master", - "ios": { - "podName": "react-native-parallax-view" - }, - "tvos": { - "podName": "react-native-parallax-view" - }, - "androidtv": { - "package": "com.reactlibrary.ParallaxViewPackage" + "amazon-cognito-identity-js": { + "android": { + "package": "com.amazonaws.RNAWSCognitoPackage" }, - "firetv": { - "package": "com.reactlibrary.ParallaxViewPackage" + "ios": { + "podName": "RNAWSCognito" }, - "android": { - "package": "com.reactlibrary.ParallaxViewPackage" - } + "version": "5.2.11" }, - "react-native-android-open-settings": { - "version": "1.3.0", + "aws-amplify": "4.3.13", + "axios": "0.24.0", + "crashlytics": { "android": { - "package": "com.levelasquez.androidopensettings.AndroidOpenSettingsPackage" - } - }, - "react-native-vector-icons": { - "version": "8.1.0", - "ios": { - "podName": "RNVectorIcons", - "ignoreProjectFonts": [ - "FontAwesome.ttf", - "FontAwesome5_Solid.ttf", - "FontAwesome5_Regular.ttf", - "FontAwesome5_Brands.ttf", - "Feather.ttf", - "AntDesign.ttf", - "Entypo.ttf", - "EvilIcons.ttf", - "Foundation.ttf", - "Ionicons.ttf", - "MaterialCommunityIcons.ttf", - "MaterialIcons.ttf", - "Octicons.ttf", - "SimpleLineIcons.ttf", - "Zocial.ttf" - ] - }, - "tvos": { - "podName": "RNVectorIcons", - "ignoreProjectFonts": [ - "FontAwesome.ttf", - "FontAwesome5_Solid.ttf", - "FontAwesome5_Regular.ttf", - "FontAwesome5_Brands.ttf", - "Feather.ttf", - "AntDesign.ttf", - "Entypo.ttf", - "EvilIcons.ttf", - "Foundation.ttf", - "Ionicons.ttf", - "MaterialCommunityIcons.ttf", - "MaterialIcons.ttf", - "Octicons.ttf", - "SimpleLineIcons.ttf", - "Zocial.ttf" - ] - }, - "android": { - "package": "com.oblador.vectoricons.VectorIconsPackage" - }, - "androidtv": { - "package": "com.oblador.vectoricons.VectorIconsPackage" - }, - "firetv": { - "package": "com.oblador.vectoricons.VectorIconsPackage" - }, - "androidwear": { - "package": "com.oblador.vectoricons.VectorIconsPackage" - }, - "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "react-native-sound": { - "version": "0.11.0", - "ios": { - "podName": "RNSound" + "mainActivity": { + "createMethods": [ + "val core = CrashlyticsCore.Builder().listener {}.build()", + "val crashlyticsKit = Crashlytics.Builder().core(core).build()", + "Fabric.with(this, crashlyticsKit)" + ], + "imports": [ + "com.crashlytics.android.Crashlytics", + "com.crashlytics.android.core.CrashlyticsCore" + ] + }, + "skipImplementation": true }, - "android": { - "package": "com.zmxv.RNSound.RNSoundPackage" - } - }, - "react-native-audio": { - "version": "4.3.0", + "deprecated": "crashlytics plugin is deprecated use Crashlytics (uppercase) in combination with Firebase", "ios": { - "podName": "RNAudio" - }, - "tvos": { - "podName": "RNAudio" - }, - "android": { - "package": "com.rnim.rn.audio.ReactNativeAudioPackage" - }, - "androidtv": { - "package": "com.rnim.rn.audio.ReactNativeAudioPackage" + "appDelegateImports": [ + "Crashlytics" + ], + "podName": "Crashlytics", + "version": "3.13.4" }, - "firetv": { - "package": "com.rnim.rn.audio.ReactNativeAudioPackage" - } + "no-npm": true }, - "react-native-webview-bridge": { - "version": "0.40.1", - "ios": { - "podName": "react-native-webview-bridge" - }, + "Crashlytics": { "android": { - "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" + "mainActivity": { + "createMethods": [ + "val core = CrashlyticsCore.Builder().listener {}.build()", + "val crashlyticsKit = Crashlytics.Builder().core(core).build()", + "Fabric.with(this, crashlyticsKit)" + ], + "imports": [ + "com.crashlytics.android.Crashlytics", + "com.crashlytics.android.core.CrashlyticsCore" + ] + }, + "skipImplementation": true }, "androidtv": { - "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" + "mainActivity": { + "createMethods": [ + "val core = CrashlyticsCore.Builder().listener {}.build()", + "val crashlyticsKit = Crashlytics.Builder().core(core).build()", + "Fabric.with(this, crashlyticsKit)" + ], + "imports": [ + "com.crashlytics.android.Crashlytics", + "com.crashlytics.android.core.CrashlyticsCore" + ] + }, + "skipImplementation": true }, "firetv": { - "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" - } - }, - "react-native-picker": { - "version": "4.3.7", - "ios": { - "podName": "Picker" - }, - "android": { - "package": "com.beefe.picker.PickerViewPackage" - }, - "androidtv": { - "package": "com.beefe.picker.PickerViewPackage" + "mainActivity": { + "createMethods": [ + "val core = CrashlyticsCore.Builder().listener {}.build()", + "val crashlyticsKit = Crashlytics.Builder().core(core).build()", + "Fabric.with(this, crashlyticsKit)" + ], + "imports": [ + "com.crashlytics.android.Crashlytics", + "com.crashlytics.android.core.CrashlyticsCore" + ] + }, + "skipImplementation": true }, - "firetv": { - "package": "com.beefe.picker.PickerViewPackage" - } - }, - "react-native-permissions": { - "version": "1.1.1", "ios": { - "podName": "ReactNativePermissions" + "podName": "Crashlytics", + "version": "3.13.4", + "xcodeproj": { + "buildPhases": [ + { + "shellPath": "/bin/sh", + "shellScript": "\"${PODS_ROOT}/Fabric/upload-symbols\" -gsp \"${PROJECT_DIR}/RNVApp/GoogleService-Info.plist\" -p ios \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"" + } + ] + } }, - "macos": { - "podName": "ReactNativePermissions" - } + "no-npm": true }, - "react-native-device-info": { - "version": "5.6.1", - "ios": { - "podName": "RNDeviceInfo" - }, - "tvos": { - "podName": "RNDeviceInfo" - }, + "deepmerge": "4.2.2", + "detox": { "android": { - "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" - }, - "androidtv": { - "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" - }, - "firetv": { - "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" - }, - "androidwear": { - "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" + "defaultConfig": [ + "testBuildType System.getProperty('testBuildType', 'debug')", + "testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'" + ] }, - "webpack": { - "modulePaths": [ - "node_modules/react-native-device-info" - ], - "moduleAliases": { - "react-native-device-info": { - "projectPath": "node_modules/react-native-device-info" + "BuildGradle": { + "allprojects": { + "repositories": { + " maven { url \"{{resolvePackage(detox)}}/Detox-android\" }": true } } - } - }, - "react-native-material-dialog": { - "version": "0.7.6" - }, - "@react-native-community/datetimepicker": { - "version": "2.1.0" - }, - "@mapbox/react-native-mapbox-gl": { - "version": "github:nitaliano/react-native-mapbox-gl", - "ios": { - "podName": "react-native-mapbox-gl" - }, - "tvos": { - "podName": "react-native-mapbox-gl" - }, - "android": { - "projectName": "mapbox-react-native-mapbox-gl", - "package": "com.mapbox.rctmgl.RCTMGLPackage", - "path": "node_modules/@mapbox/react-native-mapbox-gl/android/rctmgl", - "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }" }, - "androidtv": { - "projectName": "mapbox-react-native-mapbox-gl", - "package": "com.mapbox.rctmgl.RCTMGLPackage", - "path": "node_modules/@mapbox/react-native-mapbox-gl/android/rctmgl", - "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }" - }, - "firetv": { - "projectName": "mapbox-react-native-mapbox-gl", - "package": "com.mapbox.rctmgl.RCTMGLPackage", - "path": "node_modules/@mapbox/react-native-mapbox-gl/android/rctmgl", - "implementation": " implementation (project(':mapbox-react-native-mapbox-gl')) {\n implementation ('com.squareup.okhttp3:okhttp:3.6.0') {\n force = true\n }\n }" - } + "implementation": "androidTestImplementation('com.wix:detox:+') { transitive = true }\nandroidTestImplementation 'junit:junit:4.12'", + "version": "19.5.3" }, - "react-native-image-resizer": { - "version": "1.0.0", - "ios": { - "podName": "react-native-image-resizer" - }, - "tvos": { - "podName": "react-native-image-resizer" - }, + "fabric": { "android": { - "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'io.fabric.tools:gradle:1.25.4'" + ] + }, + "repositories": [ + "maven { url 'https://maven.fabric.io/public' }" + ] + }, + "BuildGradle": { + "allprojects": { + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + }, + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.25.4'": true + }, + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + } + }, + "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", + "mainActivity": { + "imports": [ + "io.fabric.sdk.android.Fabric" + ] + } }, "androidtv": { - "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" - }, - "firetv": { - "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" - } - }, - "react-native-photo-editor": { - "version": "1.0.5", - "pluginDependencies": { - "iOSPhotoEditor": "source:rnv" - }, - "ios": { - "podName": "RNPhotoEditor", - "path": "node_modules/react-native-photo-editor/ios" - }, - "android": { - "package": "ui.photoeditor.RNPhotoEditorPackage" - }, - "androidtv": { - "package": "ui.photoeditor.RNPhotoEditorPackage" - }, - "firetv": { - "package": "ui.photoeditor.RNPhotoEditorPackage" - } - }, - "react-native-sqlite-storage": { - "version": "5.0.0", - "android": { - "path": "/node_modules/react-native-sqlite-storage/platforms/android", - "package": "org.pgsqlite.SQLitePluginPackage" - }, - "androidtv": { - "path": "/node_modules/react-native-sqlite-storage/platforms/android", - "package": "org.pgsqlite.SQLitePluginPackage" - }, - "firetv": { - "path": "/node_modules/react-native-sqlite-storage/platforms/android", - "package": "org.pgsqlite.SQLitePluginPackage" - }, - "ios": { - "podName": "react-native-sqlite-storage", - "path": "node_modules/react-native-sqlite-storage" - }, - "tvos": { - "podName": "react-native-sqlite-storage", - "path": "node_modules/react-native-sqlite-storage" - } - }, - "react-native-dynamic-fonts": { - "version": "0.3.2", - "ios": { - "podName": "DynamicFonts" - }, - "android": { - "package": "org.th317erd.react.DynamicFontsPackage" - } - }, - "react-native-signature-capture": { - "version": "0.4.9", - "ios": { - "podName": "react-native-signature-capture" - }, - "macos": { - "podName": "react-native-signature-capture" - }, - "android": { - "projectName": "reactnativesignaturecapture", - "package": "com.rssignaturecapture.RSSignatureCapturePackage" - }, - "androidtv": { - "projectName": "reactnativesignaturecapture", - "package": "com.rssignaturecapture.RSSignatureCapturePackage" - }, - "firetv": { - "projectName": "reactnativesignaturecapture", - "package": "com.rssignaturecapture.RSSignatureCapturePackage" - } - }, - "react-native-contacts": { - "version": "3.1.2", - "ios": { - "podName": "react-native-contacts" - }, - "android": { - "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" - }, - "androidtv": { - "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" - }, - "firetv": { - "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" - } - }, - "react-native-orientation-locker": { - "version": "1.1.5", - "ios": { - "podName": "react-native-orientation-locker", - "appDelegateImports": [ - "react_native_orientation_locker" - ], - "appDelegateMethods": { - "application": { - "supportedInterfaceOrientationsFor": [ - "Orientation.getOrientation();" + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'io.fabric.tools:gradle:1.25.4'" ] - } - } - }, - "android": { - "package": "org.wonday.orientation.OrientationPackage", - "mainActivity": { - "imports": [ - "android.content.res.Configuration" - ], - "methods": [ - "override fun onConfigurationChanged(newConfig:Configuration) {", - " super.onConfigurationChanged(newConfig)", - " val intent = Intent(\"onConfigurationChanged\")", - " intent.putExtra(\"newConfig\", newConfig)", - " this.sendBroadcast(intent)", - "}" + }, + "repositories": [ + "maven { url 'https://maven.fabric.io/public' }" ] - } - } - }, - "react-native-reanimated": { - "version": "1.13.3", - "ios": { - "podName": "RNReanimated" - }, - "tvos": { - "podName": "RNReanimated" - }, - "android": { - "package": "com.swmansion.reanimated.ReanimatedPackage" - }, - "androidtv": { - "package": "com.swmansion.reanimated.ReanimatedPackage" - }, - "firetv": { - "package": "com.swmansion.reanimated.ReanimatedPackage" - }, - "androidwear": { - "package": "com.swmansion.reanimated.ReanimatedPackage" - }, - "webpack": { - "modulePaths": true, - "moduleAliases": { - "react-native-reanimated": { - "projectPath": "node_modules/react-native-reanimated/web/Animated.js" + }, + "BuildGradle": { + "allprojects": { + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + }, + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.25.4'": true + }, + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } } - } - } - }, - "react-native-screens": { - "version": "2.2.0", - "ios": { - "podName": "RNScreens" - }, - "tvos": { - "podName": "RNScreens" - }, - "android": { - "package": "com.swmansion.rnscreens.RNScreensPackage" - }, - "androidtv": { - "package": "com.swmansion.rnscreens.RNScreensPackage" + }, + "skipImplementation": true }, + "deprecated": "fabric plugin is deprecated use Fabric (uppercase) in combination with Firebase", "firetv": { - "package": "com.swmansion.rnscreens.RNScreensPackage" - }, - "webpack": { - "modulePaths": true - } - }, - "iOSPhotoEditor": { - "no-npm": true, - "ios": { - "podName": "iOSPhotoEditor", - "git": "https://github.com/prscX/photo-editor", - "commit": "4924e9ec984d25d03644e58aa148282642171de9", - "modular_headers": true - } - }, - "ios-photo-editor": { - "deprecated": "ios-photo-editor is DEPRECATED. use iOSPhotoEditor instead", - "no-npm": true, - "ios": { - "podName": "iOSPhotoEditor", - "git": "https://github.com/prscX/photo-editor", - "commit": "fa8894c992dedb431d696eb43ac4cc4ba847b4b8", - "modular_headers": true - } - }, - "google-maps": { - "no-npm": true, - "MAPS_API_KEY": "define_your_own_api_key", - "props": { - "API_KEY": "" - }, - "ios": { - "podName": "GoogleMaps", - "appDelegateImports": [ - "GoogleMaps" - ], - "version": "2.5.0", - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - "GMSServices.provideAPIKey(\"{{props.API_KEY}}\")" + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'io.fabric.tools:gradle:1.25.4'" ] + }, + "repositories": [ + "maven { url 'https://maven.fabric.io/public' }" + ] + }, + "BuildGradle": { + "allprojects": { + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + }, + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.25.4'": true + }, + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } } - } - } - }, - "crashlytics": { - "no-npm": true, - "deprecated": "crashlytics plugin is deprecated use Crashlytics (uppercase) in combination with Firebase", - "ios": { - "podName": "Crashlytics", - "appDelegateImports": [ - "Crashlytics" - ], - "version": "3.13.4" + }, + "skipImplementation": true }, - "android": { - "skipImplementation": true, - "mainActivity": { - "imports": [ - "com.crashlytics.android.Crashlytics", - "com.crashlytics.android.core.CrashlyticsCore" - ], - "createMethods": [ - "val core = CrashlyticsCore.Builder().listener {}.build()", - "val crashlyticsKit = Crashlytics.Builder().core(core).build()", - "Fabric.with(this, crashlyticsKit)" - ] - } - } - }, - "fabric": { - "no-npm": true, - "deprecated": "fabric plugin is deprecated use Fabric (uppercase) in combination with Firebase", "ios": { - "podName": "Fabric", "appDelegateImports": [ "Fabric" ], - "version": "1.10.2", "appDelegateMethods": { "application": { "didFinishLaunchingWithOptions": [ @@ -1364,24 +1216,57 @@ } ] } - } + }, + "podName": "Fabric", + "version": "1.10.2" }, + "no-npm": true + }, + "Fabric": { "android": { - "BuildGradle": { - "buildscript": { + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, + "build.gradle": { + "buildscript": { + "dependencies": [ + "classpath 'io.fabric.tools:gradle:1.25.4'" + ] + }, + "repositories": [ + "maven { url 'https://maven.fabric.io/public' }" + ] + }, + "BuildGradle": { + "allprojects": { "repositories": { "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.25.4'": true } }, - "allprojects": { + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.25.4'": true + }, "repositories": { "maven { url 'https://maven.fabric.io/public' }": true } } }, + "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", + "mainActivity": { + "imports": [ + "io.fabric.sdk.android.Fabric" + ] + } + }, + "androidtv": { + "app/build.gradle": { + "apply": [ + "plugin: 'io.fabric'" + ] + }, "build.gradle": { "buildscript": { "dependencies": [ @@ -1392,348 +1277,1392 @@ "maven { url 'https://maven.fabric.io/public' }" ] }, - "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", - "app/build.gradle": { - "apply": [ - "plugin: 'io.fabric'" - ] + "BuildGradle": { + "allprojects": { + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + }, + "buildscript": { + "dependencies": { + "classpath 'io.fabric.tools:gradle:1.25.4'": true + }, + "repositories": { + "maven { url 'https://maven.fabric.io/public' }": true + } + } }, + "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", "mainActivity": { "imports": [ "io.fabric.sdk.android.Fabric" ] } }, - "androidtv": { - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.25.4'": true + "ios": { + "podName": "Fabric", + "version": "1.10.2", + "xcodeproj": { + "buildPhases": [ + { + "shellPath": "/bin/sh", + "shellScript": "\"${PODS_ROOT}/Fabric/run\"" } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true + ] + } + }, + "no-npm": true + }, + "firebase-core": { + "deprecated": "firebase-core plugin is deprecated use Firebase/ instead", + "ios": { + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "FirebaseApp.configure()", + "if #available(iOS 10.0, *) {", + " // For iOS 10 display notification (sent via APNS)", + " UNUserNotificationCenter.current().delegate = self", + " let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]", + " UNUserNotificationCenter.current().requestAuthorization(", + " options: authOptions,", + " completionHandler: {_, _ in })", + "} else {", + " let settings: UIUserNotificationSettings =", + " UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)", + " application.registerUserNotificationSettings(settings)", + "}", + "application.registerForRemoteNotifications()" + ] + } + }, + "podNames": [ + "Firebase/Core', '~> 6.3.0", + "Firebase/Analytics', '~> 6.3.0", + "Firebase/Auth', '~> 6.3.0", + "Firebase/Database', '~> 6.3.0", + "Firebase/DynamicLinks', '~> 6.3.0", + "Firebase/Messaging', '~> 6.3.0", + "Firebase/RemoteConfig', '~> 6.3.0", + "Firebase/Storage', '~> 6.3.0" + ] + }, + "no-npm": true + }, + "Firebase/Analytics": { + "ios": { + "appDelegateImports": [ + "Firebase" + ], + "podNames": [ + "Firebase/Analytics', '~> 6.3.0" + ] + }, + "no-npm": true + }, + "Firebase/Core": { + "ios": { + "appDelegateImports": [ + "Firebase" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "FirebaseApp.configure()", + "if #available(iOS 10.0, *) {", + " // For iOS 10 display notification (sent via APNS)", + " UNUserNotificationCenter.current().delegate = self", + " let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]", + " UNUserNotificationCenter.current().requestAuthorization(", + " options: authOptions,", + " completionHandler: {_, _ in })", + "} else {", + " let settings: UIUserNotificationSettings =", + " UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)", + " application.registerUserNotificationSettings(settings)", + "}", + "application.registerForRemoteNotifications()" + ] + } + }, + "podNames": [ + "Firebase/Core', '~> 6.3.0" + ] + }, + "no-npm": true + }, + "Firebase/Messaging": { + "ios": { + "appDelegateImports": [ + "Firebase" + ], + "podNames": [ + "Firebase/Messaging', '~> 6.3.0" + ] + }, + "no-npm": true + }, + "google-maps": { + "ios": { + "appDelegateImports": [ + "GoogleMaps" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "GMSServices.provideAPIKey(\"{{props.API_KEY}}\")" + ] + } + }, + "podName": "GoogleMaps", + "version": "6.1.1" + }, + "MAPS_API_KEY": "define_your_own_api_key", + "no-npm": true, + "props": { + "API_KEY": "" + } + }, + "GoogleTagManager": { + "android": { + "implementation": "implementation('com.google.android.gms:play-services-tagmanager:18.0.1')" + }, + "ios": { + "podNames": [ + "GoogleTagManager', '~> 7.4.1" + ], + "xcodeproj": { + "resourceFiles": [ + "RNVApp/container" + ] + } + }, + "no-npm": true + }, + "hash-source": "1.0.4", + "ios-photo-editor": { + "deprecated": "ios-photo-editor is DEPRECATED. use iOSPhotoEditor instead", + "ios": { + "commit": "fa8894c992dedb431d696eb43ac4cc4ba847b4b8", + "git": "https://github.com/prscX/photo-editor", + "modular_headers": true, + "podName": "iOSPhotoEditor" + }, + "no-npm": true + }, + "iOSPhotoEditor": { + "ios": { + "commit": "4924e9ec984d25d03644e58aa148282642171de9", + "git": "https://github.com/prscX/photo-editor", + "modular_headers": true, + "podName": "iOSPhotoEditor" + }, + "no-npm": true + }, + "loader-utils": { + "version": "2.0.0" + }, + "lottie-react-native": { + "android": { + "package": "com.airbnb.android.react.lottie.LottiePackage", + "path": "{{PLUGIN_ROOT}}/src/android" + }, + "androidtv": { + "package": "com.airbnb.android.react.lottie.LottiePackage", + "path": "{{PLUGIN_ROOT}}/src/android" + }, + "firetv": { + "package": "com.airbnb.android.react.lottie.LottiePackage", + "path": "{{PLUGIN_ROOT}}/src/android" + }, + "ios": { + "podName": "lottie-react-native" + }, + "macos": { + "podName": "lottie-react-native" + }, + "npm": { + "lottie-ios": "3.4.1" + }, + "tvos": { + "podName": "lottie-react-native" + }, + "version": "5.1.5" + }, + "metro": { + "no-npm": true + }, + "native-base": { + "pluginDependencies": { + "react-native-vector-icons": "source:rnv" + }, + "version": "2.12.1" + }, + "native-base-shoutem-theme": { + "version": "0.2.3", + "webpack": { + "modulePaths": true + } + }, + "next": { + "version": "12.1.6" + }, + "next-seo": "4.28.1", + "RCTLinkingIOS": { + "ios": { + "appDelegateMethods": { + "application": { + "open": [ + "RCTLinkingManager.application(app, open: url, options: options)" + ] + } + } + }, + "no-npm": true + }, + "RCTPushNotification": { + "deprecated": "RCTPushNotification is DEPRECATED. use @react-native-community/push-notification-ios instead", + "ios": { + "appDelegateMethods": { + "application": { + "didFailToRegisterForRemoteNotificationsWithError": [ + "RCTPushNotificationManager.didFailToRegisterForRemoteNotificationsWithError(error)" + ], + "didReceive": [ + "RCTPushNotificationManager.didReceive(notification)" + ], + "didReceiveRemoteNotification": [ + "RCTPushNotificationManager.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)" + ], + "didRegister": [ + "RCTPushNotificationManager.didRegister(notificationSettings)" + ], + "didRegisterForRemoteNotificationsWithDeviceToken": [ + "RCTPushNotificationManager.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)" + ] + }, + "userNotificationCenter": { + "willPresent": [ + "completionHandler([.alert, .badge, .sound])" + ] + } + } + }, + "no-npm": true + }, + "react": { + "version": "18.2.0" + }, + "react-art": { + "version": "18.2.0" + }, + "react-dom": { + "version": "18.2.0" + }, + "react-hot-loader": { + "version": "4.3.12" + }, + "react-native": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": "uriScheme", + "android:value": "{{props.URL_SCHEME}}", + "tag": "meta-data" + }, + { + "android:name": ".MainActivity", + "children": [ + { + "children": [ + { + "android:scheme": "{{props.URL_SCHEME}}", + "tag": "data" + } + ], + "tag": "intent-filter" + } + ], + "tag": "activity" + } + ], + "tag": "application" + } + ] + }, + "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n }", + "path": "react-native" + }, + "androidtv": { + "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", + "path": "react-native" + }, + "androidwear": { + "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", + "path": "react-native" + }, + "firetv": { + "implementation": "implementation ('com.facebook.react:react-native:+') {\n exclude group: 'com.android.support' \n exclude group: 'javax.inject' \n exclude group: 'com.squareup.okhttp3', module: 'okhttp' \n exclude group: 'com.squareup.okio' \n }", + "path": "react-native" + }, + "ios": { + "plist": { + "CFBundleURLTypes": [ + { + "CFBundleURLName": "{{props.URL_NAME}}", + "CFBundleURLSchemes": [ + "{{props.URL_SCHEME}}" + ] + } + ] + }, + "Podfile": { + "sources": [ + "https://github.com/CocoaPods/Specs.git" + ] + } + }, + "macos": { + "plist": { + "CFBundleURLTypes": [ + { + "CFBundleURLName": "{{props.URL_NAME}}", + "CFBundleURLSchemes": [ + "{{props.URL_SCHEME}}" + ] + } + ] + } + }, + "props": { + "URL_NAME": "", + "URL_SCHEME": "" + }, + "version": "0.67.2" + }, + "react-native-actionsheet": { + "version": "2.4.2" + }, + "react-native-activity-view": { + "ios": { + "podName": "react-native-activity-view" + }, + "version": "0.2.11", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-airplay-ios": { + "ios": { + "path": "{{PLUGIN_ROOT}}/ios", + "podName": "RNAirplay" + }, + "version": "github:CHaNGeTe/react-native-airplay-ios#feature/cocoapods-support" + }, + "react-native-android-open-settings": { + "android": { + "package": "com.levelasquez.androidopensettings.AndroidOpenSettingsPackage" + }, + "version": "1.3.0" + }, + "react-native-animatable": { + "version": "1.3.3", + "webpack": { + "nextTranspileModules": [ + "react-native-animatable" + ] + } + }, + "react-native-audio": { + "android": { + "package": "com.rnim.rn.audio.ReactNativeAudioPackage" + }, + "androidtv": { + "package": "com.rnim.rn.audio.ReactNativeAudioPackage" + }, + "firetv": { + "package": "com.rnim.rn.audio.ReactNativeAudioPackage" + }, + "ios": { + "podName": "RNAudio" + }, + "macos": { + "podName": "RNAudio" + }, + "tvos": { + "podName": "RNAudio" + }, + "version": "4.3.0" + }, + "react-native-auth0": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": ".MainActivity", + "children": [ + { + "children": [ + { + "android:host": "{{props.DOMAIN}}", + "android:pathPrefix": "/android/${applicationId}/callback", + "android:scheme": "${applicationId}", + "tag": "data" + } + ], + "tag": "intent-filter" + } + ], + "tag": "activity" + } + ], + "tag": "application" + } + ] + }, + "package": "com.auth0.react.A0Auth0Package", + "path": "{{PLUGIN_ROOT}}/android" + }, + "ios": { + "path": "{{PLUGIN_ROOT}}/ios", + "plist": { + "CFBundleURLTypes": [ + { + "CFBundleTypeRole": "None", + "CFBundleURLName": "auth0", + "CFBundleURLSchemes": [ + "$(PRODUCT_BUNDLE_IDENTIFIER)" + ] + } + ] + }, + "podName": "A0Auth0" + }, + "pluginDependencies": { + "RCTLinkingIOS": "source:rnv" + }, + "props": { + "DOMAIN": "" + }, + "version": "2.1.0" + }, + "react-native-autocomplete-input": { + "version": "5.3.2", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-biometrics": { + "android": { + "package": "com.rnbiometrics.ReactNativeBiometricsPackage", + "projectName": "react-native-biometrics" + }, + "ios": { + "podName": "react-native-biometrics" + }, + "version": "3.0.1" + }, + "react-native-ble-manager": { + "android": { + "package": "it.innove.BleManagerPackage" + }, + "androidtv": { + "package": "it.innove.BleManagerPackage" + }, + "androidwear": { + "package": "it.innove.BleManagerPackage" + }, + "firetv": { + "package": "it.innove.BleManagerPackage" + }, + "ios": { + "package": "it.innove.BleManagerPackage" + }, + "version": "7.4.2" + }, + "react-native-blob-util": { + "android": { + "package": "com.ReactNativeBlobUtil.ReactNativeBlobUtilPackage" + }, + "androidtv": { + "package": "com.ReactNativeBlobUtil.ReactNativeBlobUtilPackage" + }, + "ios": { + "podName": "react-native-blob-util" + }, + "macos": { + "podName": "react-native-blob-util" + }, + "version": "0.17.3" + }, + "react-native-cached-image": { + "version": "1.4.3" + }, + "react-native-call-log": { + "android": { + "package": "com.wscodelabs.callLogs.CallLogPackage" + }, + "androidtv": { + "package": "com.wscodelabs.callLogs.CallLogPackage" + }, + "firetv": { + "package": "com.wscodelabs.callLogs.CallLogPackage" + }, + "version": "2.1.2" + }, + "react-native-camera": { + "android": { + "app/build.gradle": { + "defaultConfig": [ + "missingDimensionStrategy 'react-native-camera', 'general'" + ] + }, + "package": "org.reactnative.camera.RNCameraPackage" + }, + "androidtv": { + "app/build.gradle": { + "defaultConfig": [ + "missingDimensionStrategy 'react-native-camera', 'general'" + ] + }, + "package": "org.reactnative.camera.RNCameraPackage" + }, + "firetv": { + "app/build.gradle": { + "defaultConfig": [ + "missingDimensionStrategy 'react-native-camera', 'general'" + ] + }, + "package": "org.reactnative.camera.RNCameraPackage" + }, + "ios": { + "podName": "react-native-camera" + }, + "macos": { + "podName": "react-native-camera" + }, + "version": "4.2.1", + "webpack": { + "modulePaths": true + } + }, + "react-native-carplay": { + "ios": { + "appDelegateExtensions": [ + "CPApplicationDelegate" + ], + "appDelegateImports": [ + "CarPlay", + "react_native_carplay" + ], + "appDelegateMethods": { + "application": { + "didConnectCarInterfaceController": [ + "RNCarPlay.connect(with: interfaceController, window: window)" + ], + "didDisconnectCarInterfaceController": [ + "RNCarPlay.disconnect()" + ] + } + }, + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-carplay" + }, + "version": "2.0.0" + }, + "react-native-circular-progress": { + "version": "1.3.0", + "webpack": { + "modulePaths": true + } + }, + "react-native-community-geolocation": { + "android": { + "package": "com.agontuk.RNFusedLocation.RNFusedLocationPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "ios": { + "podName": "react-native-geolocation-service" + }, + "macos": { + "podName": "react-native-geolocation-service" + }, + "version": "npm:react-native-geolocation-service@5.3.1", + "web": { + "enabled": true + } + }, + "react-native-contacts": { + "android": { + "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" + }, + "androidtv": { + "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" + }, + "firetv": { + "package": "com.rt2zz.reactnativecontacts.ReactNativeContacts" + }, + "ios": { + "podName": "react-native-contacts" + }, + "macos": { + "podName": "react-native-contacts" + }, + "version": "7.0.5" + }, + "react-native-datepicker": { + "ios": { + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "if #available(iOS 14, *) { let picker = UIDatePicker.appearance(); picker.preferredDatePickerStyle = .wheels}" + ] + } + } + }, + "version": "1.7.2" + }, + "react-native-device-info": { + "android": { + "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" + }, + "androidtv": { + "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" + }, + "androidwear": { + "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" + }, + "firetv": { + "package": "com.learnium.RNDeviceInfo.RNDeviceInfo" + }, + "ios": { + "podName": "RNDeviceInfo" + }, + "macos": { + "podName": "RNDeviceInfo" + }, + "tvos": { + "podName": "RNDeviceInfo" + }, + "version": "10.5.1", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-dialog": { + "version": "5.6.0", + "webpack": { + "modulePaths": true + } + }, + "react-native-document-picker": { + "android": { + "package": "com.reactnativedocumentpicker.DocumentPickerPackage" + }, + "androidtv": { + "package": "com.reactnativedocumentpicker.DocumentPickerPackage" + }, + "firetv": { + "package": "com.reactnativedocumentpicker.DocumentPickerPackage" + }, + "ios": { + "podName": "react-native-document-picker" + }, + "macos": { + "podName": "react-native-document-picker" + }, + "version": "8.2.0" + }, + "react-native-dominant-color": { + "android": { + "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "androidtv": { + "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "firetv": { + "package": "cl.hasaezs.rndominantcolor.RNDominantColorPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "version": "1.0.0" + }, + "react-native-draggable-flatlist": { + "version": "1.1.7", + "webpack": { + "modulePaths": true + } + }, + "react-native-drawer": { + "version": "0.14.4", + "webpack": { + "modulePaths": true + } + }, + "react-native-dynamic-fonts": { + "android": { + "package": "org.th317erd.react.DynamicFontsPackage" + }, + "ios": { + "podName": "DynamicFonts" + }, + "macos": { + "podName": "DynamicFonts" + }, + "version": "0.3.2" + }, + "react-native-easy-grid": { + "version": "0.2.1", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-exception-handler": { + "android": { + "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage", + "SKIP:mainApplication": { + "createMethods": [ + "ReactNativeExceptionHandlerModule.setNativeExceptionHandler(object : NativeExceptionHandlerIfc {", + "override fun handleNativeException(thread: Thread?, throwable: Throwable?, originalHandler: Thread.UncaughtExceptionHandler?) {}})" + ], + "imports": [ + "com.masteratul.exceptionhandler.NativeExceptionHandlerIfc", + "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerModule" + ] + } + }, + "androidtv": { + "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage" + }, + "firetv": { + "package": "com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage" + }, + "ios": { + "path": "{{PLUGIN_ROOT}}/ios", + "podName": "ReactNativeExceptionHandler" + }, + "version": "2.10.0" + }, + "react-native-extended-stylesheet": { + "version": "0.12.0" + }, + "react-native-extract-color": { + "ios": { + "path": "{{PLUGIN_ROOT}}/ios", + "podName": "RNExtractColor" + }, + "tvos": { + "path": "{{PLUGIN_ROOT}}/ios", + "podName": "RNExtractColor" + }, + "version": "github:kasinskas/react-native-extract-color" + }, + "react-native-fabric": { + "ios": { + "podName": "ReactNativeFabric" + }, + "tvos": { + "podName": "ReactNativeFabric" + }, + "version": "" + }, + "react-native-fast-image": { + "android": { + "package": "com.dylanvann.fastimage.FastImageViewPackage" + }, + "androidtv": { + "package": "com.dylanvann.fastimage.FastImageViewPackage" + }, + "firetv": { + "package": "com.dylanvann.fastimage.FastImageViewPackage" + }, + "ios": { + "podName": "react-native-fast-image" + }, + "tvos": { + "podName": "react-native-fast-image" + }, + "version": "7.0.2", + "webpack": { + "modulePaths": true + } + }, + "react-native-fbsdk": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": "com.facebook.sdk.ApplicationId", + "android:value": "@string/facebook_app_id", + "tag": "meta-data" + } + ], + "tag": "application" + } + ] + }, + "implementations": [ + "'com.facebook.android:facebook-android-sdk:[4,5)'" + ], + "imports": [ + "com.facebook.CallbackManager" + ], + "mainActivity": { + "createMethods": [], + "imports": [], + "resultMethods": [ + "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" + ] + }, + "mainApplication": { + "methods": [ + " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" + ] + }, + "package": "com.facebook.reactnative.androidsdk.FBSDKPackage", + "ResourceStrings": { + "children": [ + { + "child_value": "{{props.APP_ID}}", + "name": "facebook_app_id", + "tag": "string" + } + ] + } + }, + "androidtv": { + "implementations": [ + "'com.facebook.android:facebook-android-sdk:[4,5)'" + ], + "imports": [ + "com.facebook.CallbackManager" + ], + "mainActivity": { + "createMethods": [], + "imports": [], + "resultMethods": [ + "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" + ] + }, + "mainApplication": { + "methods": [ + " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" + ] + }, + "package": "com.facebook.reactnative.androidsdk.FBSDKPackage" + }, + "firetv": { + "implementations": [ + "'com.facebook.android:facebook-android-sdk:[4,5)'" + ], + "imports": [ + "com.facebook.CallbackManager" + ], + "mainActivity": { + "createMethods": [], + "imports": [], + "resultMethods": [ + "MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data)" + ] + }, + "mainApplication": { + "methods": [ + " companion object {\n @JvmStatic \n private val mCallbackManager = CallbackManager.Factory.create() \n fun getCallbackManager(): CallbackManager = mCallbackManager \n}" + ] + }, + "package": "com.facebook.reactnative.androidsdk.FBSDKPackage" + }, + "ios": { + "appDelegateImports": [ + "FBSDKCoreKit" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)" + ], + "open": [ + "ApplicationDelegate.shared.application(app, open: url, options: options)" + ] + } + }, + "plist": { + "CFBundleURLTypes": [ + { + "CFBundleTypeRole": "Editor", + "CFBundleURLSchemes": [ + "fb{{props.APP_ID}}" + ] + } + ], + "FacebookAppID": "{{props.APP_ID}}", + "FacebookDisplayName": "{{props.APP_NAME}}", + "LSApplicationQueriesSchemes": [ + "fbapi", + "fb-messenger-share-api", + "fbauth2", + "fbshareextension" + ] + }, + "podName": "react-native-fbsdk" + }, + "props": { + "APP_ID": "", + "APP_NAME": "", + "QUERIES_SCHEMES": null, + "URL_SCHEMES": null + }, + "version": "1.0.4" + }, + "react-native-fetch-blob": { + "android": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "androidtv": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "deprecated": "react-native-fetch-blob plugin is DEPRECATED. use rn-fetch-blob instead", + "firetv": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "ios": { + "podName": "react-native-fetch-blob" + }, + "macos": { + "podName": "react-native-fetch-blob" + }, + "tvos": { + "podName": "react-native-fetch-blob" + }, + "version": "0.10.8" + }, + "react-native-fingerprint-scanner": { + "android": { + "package": "com.hieuvp.fingerprint.ReactNativeFingerprintScannerPackage", + "projectName": "react-native-fingerprint-scanner" + }, + "ios": { + "podName": "react-native-fingerprint-scanner" + }, + "version": "6.0.0" + }, + "react-native-firebase": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": "com.google.firebase.messaging.default_notification_icon", + "android:resource": "@drawable/ic_stat_ic_notification", + "tag": "meta-data" + }, + { + "android:name": "uriScheme", + "android:value": "{{props.URL_SCHEME}}", + "tag": "meta-data" + }, + { + "android:name": ".MainActivity", + "children": [ + { + "children": [ + { + "android:scheme": "{{props.URL_SCHEME}}", + "tag": "data" + } + ], + "tag": "intent-filter" + } + ], + "tag": "activity" + }, + { + "android:name": "io.invertase.firebase.messaging.RNFirebaseMessagingService", + "children": [ + { + "children": [ + { + "android:name": "com.google.firebase.MESSAGING_EVENT", + "tag": "action" + } + ], + "tag": "intent-filter" + } + ], + "tag": "service" + }, + { + "android:name": "io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService", + "tag": "service" + }, + { + "android:exported": true, + "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver", + "children": [ + { + "children": [ + { + "android:name": "io.invertase.firebase.notifications.BackgroundAction", + "tag": "action" + } + ], + "tag": "intent-filter" + } + ], + "tag": "receiver" + }, + { + "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService", + "tag": "service" + }, + { + "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationReceiver", + "tag": "receiver" + }, + { + "android:enabled": true, + "android:exported": true, + "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver", + "children": [ + { + "children": [ + { + "android:name": "android.intent.action.BOOT_COMPLETED", + "tag": "action" + }, + { + "android:name": "android.intent.action.QUICKBOOT_POWERON", + "tag": "action" + }, + { + "android:name": "com.htc.intent.action.QUICKBOOT_POWERON", + "tag": "action" + }, + { + "android:name": "android.intent.category.DEFAULT", + "tag": "category" + } + ], + "tag": "intent-filter" + } + ], + "tag": "receiver" + } + ], + "tag": "application" } - } - }, - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'io.fabric.tools:gradle:1.25.4'" - ] - }, - "repositories": [ - "maven { url 'https://maven.fabric.io/public' }" ] }, - "skipImplementation": true, "app/build.gradle": { "apply": [ - "plugin: 'io.fabric'" + "plugin: 'com.google.gms.google-services'" ] - } - }, - "firetv": { - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.25.4'": true - } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - } - } }, "build.gradle": { "buildscript": { "dependencies": [ - "classpath 'io.fabric.tools:gradle:1.25.4'" + "classpath 'com.google.gms:google-services:4.2.0'" ] - }, - "repositories": [ - "maven { url 'https://maven.fabric.io/public' }" - ] + } }, - "skipImplementation": true, - "app/build.gradle": { - "apply": [ - "plugin: 'io.fabric'" - ] - } - } - }, - "Crashlytics": { - "no-npm": true, - "ios": { - "podName": "Crashlytics", - "version": "3.13.4", - "xcodeproj": { - "buildPhases": [ - { - "shellPath": "/bin/sh", - "shellScript": "\"${PODS_ROOT}/Fabric/upload-symbols\" -gsp \"${PROJECT_DIR}/RNVApp/GoogleService-Info.plist\" -p ios \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"" - } - ] - } - }, - "android": { - "skipImplementation": true, - "mainActivity": { - "imports": [ - "com.crashlytics.android.Crashlytics", - "com.crashlytics.android.core.CrashlyticsCore" - ], - "createMethods": [ - "val core = CrashlyticsCore.Builder().listener {}.build()", - "val crashlyticsKit = Crashlytics.Builder().core(core).build()", - "Fabric.with(this, crashlyticsKit)" + "implementations": [ + "'com.google.firebase:firebase-core:16.0.4'", + "'com.google.firebase:firebase-messaging:18.0.0'" + ], + "MainApplication": { + "packages": [ + "io.invertase.firebase.RNFirebasePackage", + "io.invertase.firebase.notifications.RNFirebaseNotificationsPackage", + "io.invertase.firebase.messaging.RNFirebaseMessagingPackage" ] } }, "androidtv": { - "skipImplementation": true, - "mainActivity": { - "imports": [ - "com.crashlytics.android.Crashlytics", - "com.crashlytics.android.core.CrashlyticsCore" - ], - "createMethods": [ - "val core = CrashlyticsCore.Builder().listener {}.build()", - "val crashlyticsKit = Crashlytics.Builder().core(core).build()", - "Fabric.with(this, crashlyticsKit)" - ] - } - }, - "firetv": { - "skipImplementation": true, - "mainActivity": { - "imports": [ - "com.crashlytics.android.Crashlytics", - "com.crashlytics.android.core.CrashlyticsCore" - ], - "createMethods": [ - "val core = CrashlyticsCore.Builder().listener {}.build()", - "val crashlyticsKit = Crashlytics.Builder().core(core).build()", - "Fabric.with(this, crashlyticsKit)" - ] - } - } - }, - "Fabric": { - "no-npm": true, - "ios": { - "podName": "Fabric", - "version": "1.10.2", - "xcodeproj": { - "buildPhases": [ + "AndroidManifest": { + "children": [ { - "shellPath": "/bin/sh", - "shellScript": "\"${PODS_ROOT}/Fabric/run\"" - } - ] - } - }, - "android": { - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.25.4'": true - } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true + "android:name": ".MainApplication", + "children": [ + { + "android:name": "com.google.firebase.messaging.default_notification_icon", + "android:resource": "@drawable/ic_stat_ic_notification", + "tag": "meta-data" + }, + { + "android:name": "uriScheme", + "android:value": "{{props.URL_SCHEME}}", + "tag": "meta-data" + }, + { + "android:name": ".MainActivity", + "children": [ + { + "children": [ + { + "android:scheme": "{{props.URL_SCHEME}}", + "tag": "data" + } + ], + "tag": "intent-filter" + } + ], + "tag": "activity" + }, + { + "android:name": "io.invertase.firebase.messaging.RNFirebaseMessagingService", + "children": [ + { + "children": [ + { + "android:name": "com.google.firebase.MESSAGING_EVENT", + "tag": "action" + } + ], + "tag": "intent-filter" + } + ], + "tag": "service" + }, + { + "android:name": "io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService", + "tag": "service" + }, + { + "android:exported": true, + "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver", + "children": [ + { + "children": [ + { + "android:name": "io.invertase.firebase.notifications.BackgroundAction", + "tag": "action" + } + ], + "tag": "intent-filter" + } + ], + "tag": "receiver" + }, + { + "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService", + "tag": "service" + }, + { + "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationReceiver", + "tag": "receiver" + }, + { + "android:enabled": true, + "android:exported": true, + "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver", + "children": [ + { + "children": [ + { + "android:name": "android.intent.action.BOOT_COMPLETED", + "tag": "action" + }, + { + "android:name": "android.intent.action.QUICKBOOT_POWERON", + "tag": "action" + }, + { + "android:name": "com.htc.intent.action.QUICKBOOT_POWERON", + "tag": "action" + }, + { + "android:name": "android.intent.category.DEFAULT", + "tag": "category" + } + ], + "tag": "intent-filter" + } + ], + "tag": "receiver" + } + ], + "tag": "application" } - } - }, - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'io.fabric.tools:gradle:1.25.4'" - ] - }, - "repositories": [ - "maven { url 'https://maven.fabric.io/public' }" ] }, - "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", "app/build.gradle": { "apply": [ - "plugin: 'io.fabric'" - ] - }, - "mainActivity": { - "imports": [ - "io.fabric.sdk.android.Fabric" + "plugin: 'com.google.gms.google-services'" ] - } - }, - "androidtv": { - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.25.4'": true - } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - } - } }, "build.gradle": { "buildscript": { "dependencies": [ - "classpath 'io.fabric.tools:gradle:1.25.4'" + "classpath 'com.google.gms:google-services:4.2.0'" ] - }, - "repositories": [ - "maven { url 'https://maven.fabric.io/public' }" - ] - }, - "implementation": "implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {\n transitive = true\n }", - "app/build.gradle": { - "apply": [ - "plugin: 'io.fabric'" - ] - }, - "mainActivity": { - "imports": [ - "io.fabric.sdk.android.Fabric" + } + }, + "implementations": [ + "'com.google.firebase:firebase-core:16.0.4'", + "'com.google.firebase:firebase-messaging:18.0.0'" + ], + "MainApplication": { + "packages": [ + "io.invertase.firebase.RNFirebasePackage", + "io.invertase.firebase.notifications.RNFirebaseNotificationsPackage", + "io.invertase.firebase.messaging.RNFirebaseMessagingPackage" ] } - } - }, - "Firebase/Analytics": { - "no-npm": true, - "ios": { - "podNames": [ - "Firebase/Analytics', '~> 6.3.0" - ], - "appDelegateImports": [ - "Firebase" - ] - } - }, - "Firebase/Core": { - "no-npm": true, + }, "ios": { - "podNames": [ - "Firebase/Core', '~> 6.3.0" - ], "appDelegateImports": [ "Firebase" ], "appDelegateMethods": { "application": { "didFinishLaunchingWithOptions": [ - "FirebaseApp.configure()", - "if #available(iOS 10.0, *) {", - " // For iOS 10 display notification (sent via APNS)", - " UNUserNotificationCenter.current().delegate = self", - " let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]", - " UNUserNotificationCenter.current().requestAuthorization(", - " options: authOptions,", - " completionHandler: {_, _ in })", - "} else {", - " let settings: UIUserNotificationSettings =", - " UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)", - " application.registerUserNotificationSettings(settings)", - "}", - "application.registerForRemoteNotifications()" + { + "order": -1, + "value": "FirebaseApp.configure()", + "weight": 1 + } ] } + }, + "path": "{{PLUGIN_ROOT}}/ios", + "plist": { + "CFBundleURLTypes": [ + { + "CFBundleURLName": "{{props.URL_NAME}}", + "CFBundleURLSchemes": [ + "{{props.URL_SCHEME}}" + ] + } + ] + }, + "Podfile": { + "post_install": [ + " rnfirebase = installer.pods_project.targets.find { |target| target.name == 'RNFirebase' }", + " rnfirebase.build_configurations.each do |config|", + " config.build_settings['HEADER_SEARCH_PATHS'] = '$(inherited) ${PODS_ROOT}/Headers/Public/**'", + " end" + ] + }, + "podName": "RNFirebase", + "xcodeproj": { + "resourceFiles": [ + "RNVApp/GoogleService-Info.plist" + ] } - } + }, + "props": { + "URL_NAME": "", + "URL_SCHEME": "" + }, + "version": "5.5.5" }, - "Firebase/Messaging": { - "no-npm": true, + "react-native-fs": { + "android": { + "package": "com.rnfs.RNFSPackage" + }, + "androidtv": { + "package": "com.rnfs.RNFSPackage" + }, + "firetv": { + "package": "com.rnfs.RNFSPackage" + }, "ios": { - "podNames": [ - "Firebase/Messaging', '~> 6.3.0" - ], - "appDelegateImports": [ - "Firebase" - ] - } + "podName": "RNFS" + }, + "macos": { + "podName": "RNFS" + }, + "tvos": { + "podName": "RNFS" + }, + "version": "2.20.0" }, - "firebase-core": { - "no-npm": true, - "deprecated": "firebase-core plugin is deprecated use Firebase/ instead", + "react-native-geolocation-service": { + "android": { + "package": "com.agontuk.RNFusedLocation.RNFusedLocationPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, "ios": { - "podNames": [ - "Firebase/Core', '~> 6.3.0", - "Firebase/Analytics', '~> 6.3.0", - "Firebase/Auth', '~> 6.3.0", - "Firebase/Database', '~> 6.3.0", - "Firebase/DynamicLinks', '~> 6.3.0", - "Firebase/Messaging', '~> 6.3.0", - "Firebase/RemoteConfig', '~> 6.3.0", - "Firebase/Storage', '~> 6.3.0" - ], - "appDelegateImports": [ - "Firebase" - ], - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - "FirebaseApp.configure()", - "if #available(iOS 10.0, *) {", - " // For iOS 10 display notification (sent via APNS)", - " UNUserNotificationCenter.current().delegate = self", - " let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]", - " UNUserNotificationCenter.current().requestAuthorization(", - " options: authOptions,", - " completionHandler: {_, _ in })", - "} else {", - " let settings: UIUserNotificationSettings =", - " UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)", - " application.registerUserNotificationSettings(settings)", - "}", - "application.registerForRemoteNotifications()" - ] - } - } + "podName": "react-native-geolocation-service" + }, + "macos": { + "podName": "react-native-geolocation-service" + }, + "version": "5.3.1", + "web": { + "enabled": true } }, - "lottie-react-native": { - "version": "3.2.1", + "react-native-gesture-handler": { + "source": "", + "android": { + "package": "com.swmansion.gesturehandler.RNGestureHandlerPackage", + "path": "{{PLUGIN_ROOT}}/android", + "mainActivity": null + }, + "androidtv": { + "package": "com.swmansion.gesturehandler.RNGestureHandlerPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "androidwear": { + "package": "com.swmansion.gesturehandler.RNGestureHandlerPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "firetv": { + "package": "com.swmansion.gesturehandler.RNGestureHandlerPackage", + "path": "{{PLUGIN_ROOT}}/android" + }, "ios": { - "podName": "lottie-react-native" + "podName": "RNGestureHandler" + }, + "macos": { + "podName": "RNGestureHandler" }, "tvos": { - "podName": "lottie-react-native" + "podName": "RNGestureHandler" }, + "version": "2.9.0", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-get-random-values": { "android": { - "package": "com.airbnb.android.react.lottie.LottiePackage", - "path": "node_modules/lottie-react-native/src/android" + "package": "org.linusu.RNGetRandomValuesPackage" }, "androidtv": { - "package": "com.airbnb.android.react.lottie.LottiePackage", - "path": "node_modules/lottie-react-native/src/android" + "package": "org.linusu.RNGetRandomValuesPackage" }, - "firetv": { - "package": "com.airbnb.android.react.lottie.LottiePackage", - "path": "node_modules/lottie-react-native/src/android" - } + "ios": { + "podName": "react-native-get-random-values" + }, + "macos": { + "podName": "react-native-get-random-values" + }, + "version": "1.8.0" }, "react-native-get-real-path": { - "version": "1.0.0", "android": { "package": "com.rngrp.RNGRPPackage" }, @@ -1742,766 +2671,745 @@ }, "firetv": { "package": "com.rngrp.RNGRPPackage" - } + }, + "version": "1.0.0" }, - "react-native-simple-compass": { - "version": "github:cjrorvik/react-native-simple-compass.git#9c133bd0922204bb78e5fc0b2d85dfd5a647732d", + "react-native-google-cast": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME", + "android:value": "{{configProps.id}}.CastOptionsProvider", + "tag": "meta-data" + }, + { + "android:name": "com.reactnative.googlecast.RECEIVER_APPLICATION_ID", + "android:value": "{{props.APPLICATION_ID}}", + "tag": "meta-data" + }, + { + "android:name": "com.reactnative.googlecast.RNGCExpandedControllerActivity", + "tag": "activity" + } + ], + "tag": "application" + } + ] + }, + "app/build.gradle": { + "configurations": { + "all*.exclude group: 'com.google.guava', module: 'listenablefuture'": true + } + }, + "implementations": [ + "'com.google.android.gms:play-services-cast-framework:21.0.0'", + "'com.google.guava:guava:28.0-android'" + ], + "mainActivity": { + "createMethods": [ + "CastContext.getSharedInstance(this)" + ], + "imports": [ + "com.google.android.gms.cast.framework.CastContext" + ] + }, + "MainApplication": { + "packages": [ + "com.reactnative.googlecast.GoogleCastPackage" + ] + } + }, "ios": { - "podName": "react-native-simple-compass", - "path": "node_modules/react-native-simple-compass" + "appDelegateImports": [ + "GoogleCast" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "let receiverAppID = \"{{props.APPLICATION_ID}}\"", + "let criteria = GCKDiscoveryCriteria(APPLICATION_ID: receiverAppID)", + "let options = GCKCastOptions(discoveryCriteria: criteria)", + "options.physicalVolumeButtonsWillControlDeviceVolume = true;", + "options.disableDiscoveryAutostart = false;", + "options.startDiscoveryAfterFirstTapOnCastButton = true;", + "GCKCastContext.setSharedInstanceWith(options)", + "GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true;" + ] + } + }, + "isStatic": true, + "path": "{{PLUGIN_ROOT}}", + "plist": { + "NSBluetoothAlwaysUsageDescription": "${PRODUCT_NAME} uses Bluetooth to discover nearby Cast devices.", + "NSBluetoothPeripheralUsageDescription": "${PRODUCT_NAME} uses Bluetooth to discover nearby Cast devices.", + "NSBonjourServices": [ + "_googlecast._tcp", + "_{{props.APPLICATION_ID}}._googlecast._tcp" + ], + "NSLocalNetworkUsageDescription": "${PRODUCT_NAME} uses the local network to discover Cast-enabled devices on your WiFi network.", + "NSMicrophoneUsageDescription": "${PRODUCT_NAME} uses microphone access to listen for ultrasonic tokens when pairing with nearby Cast devices." + }, + "podName": "react-native-google-cast" }, - "android": { - "package": "com.reactlibrary.RNSimpleCompassPackage" + "props": { + "APPLICATION_ID": "" }, - "androidtv": { - "package": "com.reactlibrary.RNSimpleCompassPackage" + "version": "4.6.0" + }, + "react-native-home-indicator": { + "ios": { + "appDelegateImports": [ + "react_native_home_indicator" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "vc.view = nil", + "let vc2 = HomeIndicatorViewController()", + "vc2.view = v", + "self.window?.rootViewController = vc2" + ] + } + }, + "podName": "react-native-home-indicator" }, - "firetv": { - "package": "com.reactlibrary.RNSimpleCompassPackage" - } + "version": "0.2.6" }, - "react-native-prompt-android": { - "version": "1.0.0", + "react-native-iap": { "android": { - "package": "im.shimo.react.prompt.RNPromptPackage" - }, - "androidtv": { - "package": "im.shimo.react.prompt.RNPromptPackage" + "app/build.gradle": { + "defaultConfig": [ + "missingDimensionStrategy 'store', 'play'" + ] + }, + "package": "com.dooboolab.RNIap.RNIapPackage" }, - "firetv": { - "package": "im.shimo.react.prompt.RNPromptPackage" - } + "ios": { + "podName": "RNIap" + }, + "version": "12.4.4" }, - "react-native-ble-manager": { - "version": "7.4.2", + "react-native-idle-timer": { "android": { - "package": "it.innove.BleManagerPackage" - }, - "androidwear": { - "package": "it.innove.BleManagerPackage" + "package": "com.marcshilling.idletimer.IdleTimerPackage" }, "androidtv": { - "package": "it.innove.BleManagerPackage" + "package": "com.marcshilling.idletimer.IdleTimerPackage" }, "firetv": { - "package": "it.innove.BleManagerPackage" + "package": "com.marcshilling.idletimer.IdleTimerPackage" }, "ios": { - "package": "it.innove.BleManagerPackage" - } + "podName": "react-native-idle-timer" + }, + "version": "2.1.5" }, - "react-native-call-log": { - "version": "2.1.1", + "react-native-image-cache-manager": { + "version": "1.0.1" + }, + "react-native-image-crop-picker": { "android": { - "package": "com.wscodelabs.callLogs.CallLogPackage" + "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" }, "androidtv": { - "package": "com.wscodelabs.callLogs.CallLogPackage" + "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" }, "firetv": { - "package": "com.wscodelabs.callLogs.CallLogPackage" - } + "package": "com.reactnative.ivpusic.imagepicker.PickerPackage" + }, + "ios": { + "Podfile": { + "sources": [ + "https://github.com/TimOliver/TOCropViewController.git" + ] + }, + "podName": "RNImageCropPicker" + }, + "macos": { + "Podfile": { + "sources": [ + "https://github.com/TimOliver/TOCropViewController.git" + ] + }, + "podName": "RNImageCropPicker" + }, + "pluginDependencies": { + "react-native-image-crop-picker-no-npm": "source:flexn" + }, + "version": "0.39.0" }, - "@react-native-firebase/app": { - "version": "8.2.0", + "react-native-image-crop-picker-no-npm": { "ios": { - "isStatic": true, - "podName": "RNFBApp" + "podName": "TOCropViewController', :git => 'https://github.com/TimOliver/TOCropViewController.git', :tag => '2.6.1" + }, + "macos": { + "podName": "TOCropViewController', :git => 'https://github.com/TimOliver/TOCropViewController.git', :tag => '2.6.1" }, + "no-npm": true + }, + "react-native-image-picker": { "android": { - "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", - "projectName": "@react-native-firebase_app", "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'com.google.gms:google-services:4.2.0'" - ] - } + "subprojects": "if (project.name.contains('react-native-image-picker') || project.name.contains('react-native-fetch-blob') || project.name.contains('react-native-linear-gradient') || project.name.contains('react-native-prompt-android')) {\n buildscript {\n repositories {\n jcenter()\n google()\n maven {\n url 'https://dl.bintray.com/android/android-tools/' }\n }\n }\n }" }, - "app/build.gradle": { - "apply": [ - "plugin: 'com.google.gms.google-services'" - ] - } + "package": "com.imagepicker.ImagePickerPackage" }, "androidtv": { - "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", - "projectName": "@react-native-firebase_app", - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'com.google.gms:google-services:4.2.0'" - ] - } - }, - "app/build.gradle": { - "apply": [ - "plugin: 'com.google.gms.google-services'" - ] - } + "package": "com.imagepicker.ImagePickerPackage" }, "firetv": { - "package": "io.invertase.firebase.app.ReactNativeFirebaseAppPackage", - "projectName": "@react-native-firebase_app", - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'com.google.gms:google-services:4.2.0'" - ] - } - }, - "app/build.gradle": { - "apply": [ - "plugin: 'com.google.gms.google-services'" - ] - } - } - }, - "@react-native-firebase/analytics": { - "version": "7.3.1", - "pluginDependencies": { - "@react-native-firebase/app": "source:rnv" + "package": "com.imagepicker.ImagePickerPackage" }, "ios": { - "isStatic": true, - "podName": "RNFBAnalytics", - "xcodeproj": { - "resourceFiles": [ - "RNVApp/GoogleService-Info.plist" - ] - }, - "appDelegateImports": [ - "Firebase" - ], - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - { - "order": -1, - "value": "FirebaseApp.configure()" - } - ] - } - } + "podName": "react-native-image-picker" }, + "macos": { + "podName": "react-native-image-picker" + }, + "version": "5.0.2" + }, + "react-native-image-resizer": { "android": { - "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", - "projectName": "@react-native-firebase_analytics" + "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" }, "androidtv": { - "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", - "projectName": "@react-native-firebase_analytics" + "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" }, "firetv": { - "package": "io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage", - "projectName": "@react-native-firebase_analytics" - } - }, - "@react-native-firebase/messaging": { - "version": "7.5.0", - "pluginDependencies": { - "@react-native-firebase/app": "source:rnv" + "package": "fr.bamlab.rnimageresizer.ImageResizerPackage" }, "ios": { - "isStatic": true, - "podName": "RNFBMessaging" + "podName": "react-native-image-resizer" + }, + "tvos": { + "podName": "react-native-image-resizer" }, + "version": "1.0.0" + }, + "react-native-keep-awake": { "android": { - "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", - "projectName": "@react-native-firebase_messaging" + "package": "com.corbt.keepawake.KCKeepAwakePackage" }, "androidtv": { - "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", - "projectName": "@react-native-firebase_messaging" + "package": "com.corbt.keepawake.KCKeepAwakePackage" }, "firetv": { - "package": "io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage", - "projectName": "@react-native-firebase_messaging" - } - }, - "@react-native-firebase/crashlytics": { - "version": "8.1.2", - "pluginDependencies": { - "@react-native-firebase/analytics": "source:rnv" + "package": "com.corbt.keepawake.KCKeepAwakePackage" }, "ios": { - "isStatic": true, - "podName": "RNFBCrashlytics", - "xcodeproj": { - "resourceFiles": [ - "RNVApp/GoogleService-Info.plist" - ], - "buildPhases": [ - { - "shellPath": "/bin/sh", - "shellScript": "\"${PODS_ROOT}/Fabric/run\"" - } - ] - }, - "appDelegateImports": [ - "Firebase" - ], - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - { - "order": -1, - "value": "FirebaseApp.configure()" - } - ] - } - } + "podName": "react-native-keep-awake" }, + "version": "4.0.0", + "webpack": { + "modulePaths": true + } + }, + "react-native-keyboard-aware-scroll-view": { + "version": "0.9.5", + "webpack": { + "modulePaths": true + } + }, + "react-native-keyboard-spacer": { + "version": "0.4.1" + }, + "react-native-languages": { "android": { - "package": "io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsPackage", - "projectName": "@react-native-firebase_crashlytics", - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.28.1'": true - } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - } - } - }, - "app/build.gradle": { - "apply": [ - "plugin: 'io.fabric'" - ] - } + "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" }, "androidtv": { - "package": "io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsPackage", - "projectName": "@react-native-firebase_crashlytics", - "BuildGradle": { - "buildscript": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - }, - "dependencies": { - "classpath 'io.fabric.tools:gradle:1.28.1'": true - } - }, - "allprojects": { - "repositories": { - "maven { url 'https://maven.fabric.io/public' }": true - } - } - }, - "app/build.gradle": { - "apply": [ - "plugin: 'io.fabric'" - ] - } - } + "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" + }, + "deprecated": "react-native-languages has been DEPRECATED. use react-native-localize instead", + "firetv": { + "package": "com.reactcommunity.rnlanguages.RNLanguagesPackage" + }, + "ios": { + "podName": "RNLanguages" + }, + "version": "3.0.2" }, - "@react-native-community/cameraroll": { - "version": "1.2.1", + "react-native-linear-gradient": { + "android": { + "package": "com.BV.LinearGradient.LinearGradientPackage" + }, + "androidtv": { + "package": "com.BV.LinearGradient.LinearGradientPackage" + }, + "androidwear": { + "package": "com.BV.LinearGradient.LinearGradientPackage" + }, + "firetv": { + "package": "com.BV.LinearGradient.LinearGradientPackage" + }, "ios": { - "podName": "react-native-cameraroll" + "podName": "BVLinearGradient" }, - "android": { - "package": "com.reactnativecommunity.cameraroll.CameraRollPackage" + "macos": { + "podName": "BVLinearGradient" + }, + "npm": { + "react-native-web-linear-gradient": "1.1.2" + }, + "tvos": { + "podName": "BVLinearGradient" + }, + "version": "2.6.2", + "webpack": { + "moduleAliases": { + "react-native-linear-gradient": "react-native-web-linear-gradient" + }, + "modulePaths": [ + "react-native-web-linear-gradient", + "react-native-linear-gradient" + ] } }, - "react-native-simple-shadow-view": { - "version": "1.6.3", - "android": { - "package": "com.como.RNTShadowView.ShadowViewPackage" - } + "react-native-local-mongodb": { + "version": "2.2.4" }, - "react-native-webrtc": { - "version": "1.75.2", - "ios": { - "podName": "react-native-webrtc", - "path": "node_modules/react-native-webrtc" - }, + "react-native-localize": { "android": { - "path": "node_modules/react-native-webrtc/android", - "package": "com.oney.WebRTCModule.WebRTCModulePackage" + "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" }, "androidtv": { - "path": "node_modules/react-native-webrtc/android", - "package": "com.oney.WebRTCModule.WebRTCModulePackage" + "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" }, "firetv": { - "path": "node_modules/react-native-webrtc/android", - "package": "com.oney.WebRTCModule.WebRTCModulePackage" - } - }, - "react-native-firebase": { - "version": "5.5.5", - "props": { - "URL_SCHEME": "", - "URL_NAME": "" + "package": "com.reactcommunity.rnlocalize.RNLocalizePackage" }, "ios": { - "podName": "RNFirebase", - "path": "node_modules/react-native-firebase/ios", - "xcodeproj": { - "resourceFiles": [ - "RNVApp/GoogleService-Info.plist" + "podName": "RNLocalize" + }, + "tvos": { + "podName": "RNLocalize" + }, + "version": "1.4.0" + }, + "react-native-lottie": { + "version": "3.2.1" + }, + "react-native-macos": { + "version": "0.66.33" + }, + "react-native-maps": { + "android": { + "AndroidManifest": { + "children": [ + { + "android:name": ".MainApplication", + "children": [ + { + "android:name": "com.google.android.geo.API_KEY", + "android:value": "@string/google_maps_api_key", + "tag": "meta-data" + } + ], + "tag": "application" + } ] }, - "appDelegateImports": [ - "Firebase" + "implementations": [ + "(project(':react-native-maps')){\n exclude group: 'com.google.android.gms', module: 'play-services-base' \n exclude group: 'com.google.android.gms', module: 'play-services-maps' }", + "'com.google.android.gms:play-services-base:17.2.1'", + "'com.google.android.gms:play-services-maps:17.0.0'" ], - "Podfile": { - "post_install": [ - " rnfirebase = installer.pods_project.targets.find { |target| target.name == 'RNFirebase' }", - " rnfirebase.build_configurations.each do |config|", - " config.build_settings['HEADER_SEARCH_PATHS'] = '$(inherited) ${PODS_ROOT}/Headers/Public/**'", - " end" - ] - }, - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - { - "order": -1, - "value": "FirebaseApp.configure()" - } - ] - } - }, - "plist": { - "CFBundleURLTypes": [ + "package": "com.airbnb.android.react.maps.MapsPackage", + "path": "{{PLUGIN_ROOT}}/android", + "ResourceStrings": { + "children": [ { - "CFBundleURLName": "{{props.URL_NAME}}", - "CFBundleURLSchemes": [ - "{{props.URL_SCHEME}}" - ] + "child_value": "{{props.GOOGLE_MAPS_API_KEY}}", + "name": "google_maps_api_key", + "tag": "string" } ] } }, - "android": { - "implementations": [ - "'com.google.firebase:firebase-core:16.0.4'", - "'com.google.firebase:firebase-messaging:18.0.0'" - ], - "MainApplication": { - "packages": [ - "io.invertase.firebase.RNFirebasePackage", - "io.invertase.firebase.notifications.RNFirebaseNotificationsPackage", - "io.invertase.firebase.messaging.RNFirebaseMessagingPackage" - ] - }, - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'com.google.gms:google-services:4.2.0'" - ] - } - }, - "app/build.gradle": { - "apply": [ - "plugin: 'com.google.gms.google-services'" - ] - }, + "androidtv": { "AndroidManifest": { "children": [ { - "tag": "application", "android:name": ".MainApplication", "children": [ { - "tag": "meta-data", - "android:name": "com.google.firebase.messaging.default_notification_icon", - "android:resource": "@drawable/ic_stat_ic_notification" - }, - { - "tag": "meta-data", - "android:name": "uriScheme", - "android:value": "{{props.URL_SCHEME}}" - }, - { - "tag": "activity", - "android:name": ".MainActivity", - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "data", - "android:scheme": "{{props.URL_SCHEME}}" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.messaging.RNFirebaseMessagingService", - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "com.google.firebase.MESSAGING_EVENT" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver", - "android:exported": true, - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "io.invertase.firebase.notifications.BackgroundAction" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationReceiver" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver", - "android:exported": true, - "android:enabled": true, - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "android.intent.action.BOOT_COMPLETED" - }, - { - "tag": "action", - "android:name": "android.intent.action.QUICKBOOT_POWERON" - }, - { - "tag": "action", - "android:name": "com.htc.intent.action.QUICKBOOT_POWERON" - }, - { - "tag": "category", - "android:name": "android.intent.category.DEFAULT" - } - ] - } - ] + "android:name": "com.google.android.geo.API_KEY", + "android:value": "@string/google_maps_api_key", + "tag": "meta-data" } - ] + ], + "tag": "application" + } + ] + }, + "implementations": [ + "(project(':react-native-maps')){\n exclude group: 'com.google.android.gms', module: 'play-services-base' \n exclude group: 'com.google.android.gms', module: 'play-services-maps' }", + "'com.google.android.gms:play-services-base:17.2.1'", + "'com.google.android.gms:play-services-maps:17.0.0'" + ], + "package": "com.airbnb.android.react.maps.MapsPackage", + "path": "{{PLUGIN_ROOT}}/android", + "ResourceStrings": { + "children": [ + { + "child_value": "{{props.GOOGLE_MAPS_API_KEY}}", + "name": "google_maps_api_key", + "tag": "string" } ] } }, + "ios": { + "podName": "react-native-maps" + }, + "macos": { + "podName": "react-native-maps" + }, + "version": "0.31.1" + }, + "react-native-markdown-renderer": { + "version": "3.2.8", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "react-native-material-dialog": { + "version": "0.7.6" + }, + "react-native-material-dropdown": { + "version": "0.11.1" + }, + "react-native-media-query": { + "version": "2.0.0" + }, + "react-native-modal": { + "version": "13.0.1", + "webpack": { + "modulePaths": true + } + }, + "react-native-navigation-bar-color": { + "android": { + "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" + }, "androidtv": { - "implementations": [ - "'com.google.firebase:firebase-core:16.0.4'", - "'com.google.firebase:firebase-messaging:18.0.0'" - ], - "MainApplication": { - "packages": [ - "io.invertase.firebase.RNFirebasePackage", - "io.invertase.firebase.notifications.RNFirebaseNotificationsPackage", - "io.invertase.firebase.messaging.RNFirebaseMessagingPackage" + "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" + }, + "firetv": { + "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" + }, + "version": "1.0.0" + }, + "react-native-nfc-manager": { + "android": { + "package": "community.revteltech.nfc.NfcManagerPackage" + }, + "androidtv": { + "package": "community.revteltech.nfc.NfcManagerPackage" + }, + "firetv": { + "package": "community.revteltech.nfc.NfcManagerPackage" + }, + "ios": { + "podName": "react-native-nfc-manager" + }, + "version": "1.2.2" + }, + "react-native-onfido-sdk": { + "version": "0.0.7" + }, + "react-native-orientation-locker": { + "android": { + "mainActivity": { + "imports": [ + "android.content.res.Configuration" + ], + "methods": [ + "override fun onConfigurationChanged(newConfig:Configuration) {", + " super.onConfigurationChanged(newConfig)", + " val intent = Intent(\"onConfigurationChanged\")", + " intent.putExtra(\"newConfig\", newConfig)", + " this.sendBroadcast(intent)", + "}" ] }, - "build.gradle": { - "buildscript": { - "dependencies": [ - "classpath 'com.google.gms:google-services:4.2.0'" + "package": "org.wonday.orientation.OrientationPackage" + }, + "ios": { + "appDelegateImports": [ + "react_native_orientation_locker" + ], + "appDelegateMethods": { + "application": { + "supportedInterfaceOrientationsFor": [ + "Orientation.getOrientation();" ] } }, - "app/build.gradle": { - "apply": [ - "plugin: 'com.google.gms.google-services'" - ] - }, + "podName": "react-native-orientation-locker" + }, + "version": "1.5.0" + }, + "react-native-paper": { + "version": "4.11.2" + }, + "react-native-parallax-view": { + "android": { + "package": "com.reactlibrary.ParallaxViewPackage" + }, + "androidtv": { + "package": "com.reactlibrary.ParallaxViewPackage" + }, + "firetv": { + "package": "com.reactlibrary.ParallaxViewPackage" + }, + "ios": { + "podName": "react-native-parallax-view" + }, + "tvos": { + "podName": "react-native-parallax-view" + }, + "version": "github:reactseals/react-native-parallax-view#master" + }, + "react-native-pdf": { + "android": { + "package": "org.wonday.pdf.RCTPdfView" + }, + "androidtv": { + "package": "org.wonday.pdf.RCTPdfView" + }, + "firetv": { + "package": "org.wonday.pdf.RCTPdfView" + }, + "ios": { + "podName": "react-native-pdf" + }, + "macos": { + "podName": "react-native-pdf" + }, + "version": "6.6.2" + }, + "react-native-pdf-view": { + "version": "0.3.2" + }, + "react-native-peerjs": { + "ios": { + "enabled": true + }, + "version": "1.0.2" + }, + "react-native-permissions": { + "android": { + "package": "com.zoontek.rnpermissions.RNPermissionsPackage" + }, + "androidtv": { + "package": "com.zoontek.rnpermissions.RNPermissionsPackage" + }, + "ios": { + "isStatic": true, + "podName": "RNPermissions", + "staticPods": [ + "::startsWith::Permission-" + ] + }, + "macos": { + "isStatic": true, + "podName": "RNPermissions", + "staticPods": [ + "::startsWith::Permission-" + ] + }, + "tvos": { + "isStatic": true, + "podName": "RNPermissions", + "staticPods": [ + "::startsWith::Permission-" + ] + }, + "version": "3.6.1" + }, + "react-native-permissions-pods": { + "ios": { + "podNames": [ + "Permission-BluetoothPeripheral', :path => '{{PLUGIN_ROOT}}/ios/BluetoothPeripheral", + "Permission-Calendars', :path => '{{PLUGIN_ROOT}}/ios/Calendars", + "Permission-Camera', :path => '{{PLUGIN_ROOT}}/ios/Camera", + "Permission-Contacts', :path => '{{PLUGIN_ROOT}}/ios/Contacts", + "Permission-FaceID', :path => '{{PLUGIN_ROOT}}/ios/FaceID", + "Permission-LocationAlways', :path => '{{PLUGIN_ROOT}}/ios/LocationAlways", + "Permission-LocationWhenInUse', :path => '{{PLUGIN_ROOT}}/ios/LocationWhenInUse", + "Permission-MediaLibrary', :path => '{{PLUGIN_ROOT}}/ios/MediaLibrary", + "Permission-Microphone', :path => '{{PLUGIN_ROOT}}/ios/Microphone", + "Permission-Motion', :path => '{{PLUGIN_ROOT}}/ios/Motion", + "Permission-Notifications', :path => '{{PLUGIN_ROOT}}/ios/Notifications", + "Permission-PhotoLibrary', :path => '{{PLUGIN_ROOT}}/ios/PhotoLibrary", + "Permission-Reminders', :path => '{{PLUGIN_ROOT}}/ios/Reminders", + "Permission-SpeechRecognition', :path => '{{PLUGIN_ROOT}}/ios/SpeechRecognition", + "Permission-StoreKit', :path => '{{PLUGIN_ROOT}}/ios/StoreKit" + ] + }, + "macos": { + "podNames": [ + "Permission-BluetoothPeripheral', :path => '{{PLUGIN_ROOT}}/ios/BluetoothPeripheral", + "Permission-Calendars', :path => '{{PLUGIN_ROOT}}/ios/Calendars", + "Permission-Camera', :path => '{{PLUGIN_ROOT}}/ios/Camera", + "Permission-Contacts', :path => '{{PLUGIN_ROOT}}/ios/Contacts", + "Permission-FaceID', :path => '{{PLUGIN_ROOT}}/ios/FaceID", + "Permission-LocationAlways', :path => '{{PLUGIN_ROOT}}/ios/LocationAlways", + "Permission-LocationWhenInUse', :path => '{{PLUGIN_ROOT}}/ios/LocationWhenInUse", + "Permission-MediaLibrary', :path => '{{PLUGIN_ROOT}}/ios/MediaLibrary", + "Permission-Microphone', :path => '{{PLUGIN_ROOT}}/ios/Microphone", + "Permission-Motion', :path => '{{PLUGIN_ROOT}}/ios/Motion", + "Permission-Notifications', :path => '{{PLUGIN_ROOT}}/ios/Notifications", + "Permission-PhotoLibrary', :path => '{{PLUGIN_ROOT}}/ios/PhotoLibrary", + "Permission-Reminders', :path => '{{PLUGIN_ROOT}}/ios/Reminders", + "Permission-SpeechRecognition', :path => '{{PLUGIN_ROOT}}/ios/SpeechRecognition", + "Permission-StoreKit', :path => '{{PLUGIN_ROOT}}/ios/StoreKit" + ] + }, + "no-npm": true, + "packageName": "react-native-permissions" + }, + "react-native-photo-editor": { + "android": { "AndroidManifest": { "children": [ { - "tag": "application", "android:name": ".MainApplication", "children": [ { - "tag": "meta-data", - "android:name": "com.google.firebase.messaging.default_notification_icon", - "android:resource": "@drawable/ic_stat_ic_notification" - }, - { - "tag": "meta-data", - "android:name": "uriScheme", - "android:value": "{{props.URL_SCHEME}}" - }, - { - "tag": "activity", - "android:name": ".MainActivity", - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "data", - "android:scheme": "{{props.URL_SCHEME}}" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.messaging.RNFirebaseMessagingService", - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "com.google.firebase.MESSAGING_EVENT" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver", - "android:exported": true, - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "io.invertase.firebase.notifications.BackgroundAction" - } - ] - } - ] - }, - { - "tag": "service", - "android:name": "io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationReceiver" - }, - { - "tag": "receiver", - "android:name": "io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver", - "android:exported": true, - "android:enabled": true, - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "action", - "android:name": "android.intent.action.BOOT_COMPLETED" - }, - { - "tag": "action", - "android:name": "android.intent.action.QUICKBOOT_POWERON" - }, - { - "tag": "action", - "android:name": "com.htc.intent.action.QUICKBOOT_POWERON" - }, - { - "tag": "category", - "android:name": "android.intent.category.DEFAULT" - } - ] - } - ] + "android:name": "com.ahmedadeltito.photoeditor.PhotoEditorActivity", + "tag": "activity" } - ] + ], + "tag": "application" } ] - } - } - }, - "react-native-zss-rich-text-editor": { - "version": "1.1.0", - "android": { - "skipImplementation": true, - "afterEvaluate": [ - "apply from: '../../../node_modules/react-native-zss-rich-text-editor/htmlCopy.gradle';", - "copyEditorHtmlToAppAssets(file('../../../node_modules/react-native-zss-rich-text-editor'))" - ] + }, + "package": "ui.photoeditor.RNPhotoEditorPackage" }, "androidtv": { - "skipImplementation": true, - "afterEvaluate": [ - "apply from: '../../../node_modules/react-native-zss-rich-text-editor/htmlCopy.gradle';", - "copyEditorHtmlToAppAssets(file('../../../node_modules/react-native-zss-rich-text-editor'))" - ] + "package": "ui.photoeditor.RNPhotoEditorPackage" }, "firetv": { - "skipImplementation": true, - "afterEvaluate": [ - "apply from: '../../../node_modules/react-native-zss-rich-text-editor/htmlCopy.gradle';", - "copyEditorHtmlToAppAssets(file('../../../node_modules/react-native-zss-rich-text-editor'))" - ] - } - }, - "native-base": { - "version": "2.12.1", - "pluginDependencies": { - "react-native-vector-icons": "source:rnv" - } - }, - "@react-native-community/async-storage": { - "version": "1.12.0", - "ios": { - "podName": "RNCAsyncStorage" + "package": "ui.photoeditor.RNPhotoEditorPackage" }, - "tvos": { - "podName": "RNCAsyncStorage" + "ios": { + "path": "{{PLUGIN_ROOT}}/ios", + "podName": "RNPhotoEditor" }, - "macos": { - "podName": "RNCAsyncStorage" + "pluginDependencies": { + "iOSPhotoEditor": "source:rnv" }, + "version": "1.0.5" + }, + "react-native-picker": { "android": { - "projectName": "react-native-community-async-storage", - "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage" + "package": "com.beefe.picker.PickerViewPackage" }, "androidtv": { - "projectName": "react-native-community-async-storage", - "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage" + "package": "com.beefe.picker.PickerViewPackage" }, "firetv": { - "projectName": "react-native-community-async-storage", - "package": "com.reactnativecommunity.asyncstorage.AsyncStoragePackage" + "package": "com.beefe.picker.PickerViewPackage" }, - "webpack": { - "modulePaths": true - } - }, - "react-native-web": { - "version": "0.18.0", - "pluginDependencies": { - "react": "source:rnv" - } + "ios": { + "podName": "Picker" + }, + "macos": { + "podName": "Picker" + }, + "version": "4.3.7" }, - "react-native-web-image-loader": { - "version": "0.0.5" + "react-native-picker-select": { + "version": "8.0.4" }, - "react-web-vector-icons": { - "version": "1.0.2" + "react-native-progress": { + "version": "3.5.0" }, - "react-navigation": { - "version": "3.9.2", - "webpack": { - "modulePaths": [ - "node_modules/react-navigation" - ] + "react-native-prompt-android": { + "android": { + "package": "im.shimo.react.prompt.RNPromptPackage" }, - "pluginDependencies": { - "react-navigation-stack": "source:rnv", - "react-navigation-tabs": "source:rnv", - "react-native-reanimated": "source:rnv" + "androidtv": { + "package": "im.shimo.react.prompt.RNPromptPackage" }, - "npm": { - "@react-navigation/core": "3.4.2", - "@react-navigation/native": "3.5.0", - "@react-navigation/web": "1.0.0-alpha.9" - } + "firetv": { + "package": "im.shimo.react.prompt.RNPromptPackage" + }, + "version": "1.1.0" }, - "react-navigation-stack": { - "version": "1.4.0", + "react-native-reanimated": { + "android": { + "package": "com.swmansion.reanimated.ReanimatedPackage" + }, + "androidtv": { + "package": "com.swmansion.reanimated.ReanimatedPackage" + }, + "androidwear": { + "package": "com.swmansion.reanimated.ReanimatedPackage" + }, + "firetv": { + "package": "com.swmansion.reanimated.ReanimatedPackage" + }, + "ios": { + "podName": "RNReanimated" + }, + "macos": { + "podName": "RNReanimated" + }, + "tvos": { + "podName": "RNReanimated" + }, + "version": "1.13.3", "webpack": { - "modulePaths": true, - "moduleAliases": true + "moduleAliases": true, + "modulePaths": true } }, - "react-navigation-tabs": { - "version": "2.0.0-alpha.0", + "react-native-render-html": { + "ios": { + "podName": "react-native-render-html" + }, + "version": "6.3.4", "webpack": { - "modulePaths": true, - "moduleAliases": true + "modulePaths": true } }, - "react-navigation-drawer": { - "version": "2.3.3", + "react-native-root-toast": { + "version": "3.2.1", "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "@react-navigation": { - "npm": { - "@react-navigation/core": "5.12.3", - "@react-navigation/drawer": "5.9.0", - "@react-navigation/bottom-tabs": "5.1.1", - "@react-navigation/material-bottom-tabs": "5.1.1", - "@react-navigation/native": "5.7.3", - "@react-navigation/stack": "5.9.0", - "@react-navigation/routers": "5.1.0", - "@react-navigation/material-top-tabs": "5.1.1", - "@react-navigation/web": "1.0.0-alpha.9", - "@react-native-community/masked-view": "0.1.6", - "react-native-safe-area-context": "3.1.0" - }, - "pluginDependencies": { - "react-native-screens": "source:rnv", - "react-native-safe-area-context": "source:rnv" + "nextTranspileModules": [ + "react-native-root-siblings", + "static-container" + ] } }, "react-native-safe-area-context": { - "version": "3.1.0", "android": { "package": "com.th3rdwave.safeareacontext.SafeAreaContextPackage" }, "androidtv": { "package": "com.th3rdwave.safeareacontext.SafeAreaContextPackage" }, - "firetv": { + "androidwear": { "package": "com.th3rdwave.safeareacontext.SafeAreaContextPackage" }, - "androidwear": { + "firetv": { "package": "com.th3rdwave.safeareacontext.SafeAreaContextPackage" }, "ios": { - "path": "node_modules/react-native-safe-area-context", "podName": "react-native-safe-area-context" }, "tvos": { - "path": "node_modules/react-native-safe-area-context", "podName": "react-native-safe-area-context" - } - }, - "react-native-easy-grid": { - "version": "0.2.1", - "webpack": { - "modulePaths": true, - "moduleAliases": true - } + }, + "version": "4.3.4" }, "react-native-safe-area-view": { "version": "0.14.5", @@ -2509,120 +3417,250 @@ "modulePaths": true } }, - "react-native-drawer": { - "version": "0.14.4", + "react-native-screens": { + "android": { + "package": "com.swmansion.rnscreens.RNScreensPackage" + }, + "androidtv": { + "package": "com.swmansion.rnscreens.RNScreensPackage" + }, + "firetv": { + "package": "com.swmansion.rnscreens.RNScreensPackage" + }, + "ios": { + "isStatic": true, + "podName": "RNScreens" + }, + "macos": { + "podName": "RNScreens" + }, + "tvos": { + "isStatic": true, + "podName": "RNScreens" + }, + "version": "3.20.0", "webpack": { "modulePaths": true } }, - "svgs": { - "version": "4.1.0", - "webpack": { - "moduleAliases": true - } + "react-native-select-dropdown": { + "version": "1.2.0" }, - "native-base-shoutem-theme": { - "version": "0.2.3", - "webpack": { - "modulePaths": true - } + "react-native-sensors": { + "android": { + "package": "com.sensors.RNSensorsPackage" + }, + "androidtv": { + "package": "com.sensors.RNSensorsPackage" + }, + "firetv": { + "package": "com.sensors.RNSensorsPackage" + }, + "ios": { + "podName": "RNSensors" + }, + "version": "7.3.6" }, - "react-native-keyboard-aware-scroll-view": { - "version": "0.9.1", - "webpack": { - "modulePaths": true - } + "react-native-signature-capture": { + "android": { + "package": "com.rssignaturecapture.RSSignatureCapturePackage", + "projectName": "reactnativesignaturecapture" + }, + "androidtv": { + "package": "com.rssignaturecapture.RSSignatureCapturePackage", + "projectName": "reactnativesignaturecapture" + }, + "firetv": { + "package": "com.rssignaturecapture.RSSignatureCapturePackage", + "projectName": "reactnativesignaturecapture" + }, + "ios": { + "podName": "react-native-signature-capture" + }, + "macos": null, + "version": "0.4.12" }, - "react-native-tab-view": { - "version": "2.13.0", - "webpack": { - "modulePaths": true - } + "react-native-simple-compass": { + "android": { + "package": "com.reactlibrary.RNSimpleCompassPackage" + }, + "androidtv": { + "package": "com.reactlibrary.RNSimpleCompassPackage" + }, + "firetv": { + "package": "com.reactlibrary.RNSimpleCompassPackage" + }, + "ios": { + "podName": "react-native-simple-compass" + }, + "version": "git+https://github.com/cjrorvik/react-native-simple-compass.git" }, - "static-container": { - "version": "1.3.0", + "react-native-simple-radio-button": { + "version": "2.7.3" + }, + "react-native-simple-shadow-view": { + "android": { + "package": "com.como.RNTShadowView.ShadowViewPackage" + }, + "version": "1.6.3" + }, + "react-native-snap-carousel": { + "version": "3.9.1", "webpack": { "modulePaths": true } }, - "react": { - "version": "18.1.0" - }, - "react-dom": { - "version": "18.1.0" - }, - "react-art": { - "version": "18.1.0" - }, - "react-hot-loader": { - "version": "4.3.12" - }, - "react-native-airplay-ios": { - "version": "github:CHaNGeTe/react-native-airplay-ios#feature/cocoapods-support", + "react-native-snap-scrollview": { + "android": { + "package": "com.reactlibrary.SnapScrollviewPackage" + }, + "androidtv": { + "package": "com.reactlibrary.SnapScrollviewPackage" + }, + "firetv": { + "package": "com.reactlibrary.SnapScrollviewPackage" + }, "ios": { - "path": "node_modules/react-native-airplay-ios/ios", - "podName": "RNAirplay" - } - }, - "react-native-cached-image": { - "version": "1.4.3" + "podName": "react-native-snap-scrollview" + }, + "tvos": { + "podName": "react-native-snap-scrollview" + }, + "version": "github:reactseals/react-native-snap-scrollview#master" }, - "react-native-circular-progress": { - "version": "1.3.0", - "webpack": { - "modulePaths": true - } + "react-native-sound": { + "android": { + "package": "com.zmxv.RNSound.RNSoundPackage" + }, + "ios": { + "podName": "RNSound" + }, + "macos": { + "podName": "RNSound" + }, + "version": "0.11.2" }, - "react-native-navigation-bar-color": { - "version": "1.0.0", + "react-native-splash-screen": { "android": { - "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" + "mainActivity": { + "createMethods": [ + "SplashScreen.show(this)" + ], + "imports": [ + "org.devio.rn.splashscreen.SplashScreen" + ] + }, + "package": "org.devio.rn.splashscreen.SplashScreenReactPackage" }, "androidtv": { - "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" + "mainActivity": { + "createMethods": [ + "SplashScreen.show(this)" + ], + "imports": [ + "org.devio.rn.splashscreen.SplashScreen" + ] + }, + "package": "org.devio.rn.splashscreen.SplashScreenReactPackage" }, "firetv": { - "package": "com.thebylito.navigationbarcolor.NavigationBarColorPackage" - } + "mainActivity": { + "createMethods": [ + "SplashScreen.show(this)" + ], + "imports": [ + "org.devio.rn.splashscreen.SplashScreen" + ] + }, + "package": "org.devio.rn.splashscreen.SplashScreenReactPackage" + }, + "ios": { + "appDelegateImports": [ + "react_native_splash_screen" + ], + "appDelegateMethods": { + "application": { + "didFinishLaunchingWithOptions": [ + "RNSplashScreen.show()" + ] + } + }, + "podName": "react-native-splash-screen" + }, + "version": "3.3.0" }, - "react-native-picker-select": { - "version": "6.3.3" + "react-native-sqlite-2": { + "ios": { + "podName": "RNSqlite2" + }, + "version": "3.0.1" }, - "@react-native-community/netinfo": { - "version": "5.9.7", + "react-native-sqlite-storage": { + "android": { + "package": "org.pgsqlite.SQLitePluginPackage", + "path": "{{PLUGIN_ROOT}}/platforms/android" + }, + "androidtv": { + "package": "org.pgsqlite.SQLitePluginPackage", + "path": "{{PLUGIN_ROOT}}/platforms/android" + }, + "firetv": { + "package": "org.pgsqlite.SQLitePluginPackage", + "path": "{{PLUGIN_ROOT}}/platforms/android" + }, "ios": { - "podName": "react-native-netinfo" + "podName": "react-native-sqlite-storage" }, "tvos": { - "podName": "react-native-netinfo" + "podName": "react-native-sqlite-storage" }, + "version": "6.0.1" + }, + "react-native-super-grid": { + "version": "5.0.0" + }, + "react-native-svg": { "android": { - "package": "com.reactnativecommunity.netinfo.NetInfoPackage" + "package": "com.horcrux.svg.SvgPackage" }, "androidtv": { - "package": "com.reactnativecommunity.netinfo.NetInfoPackage" + "package": "com.horcrux.svg.SvgPackage" + }, + "androidwear": { + "package": "com.horcrux.svg.SvgPackage" }, "firetv": { - "package": "com.reactnativecommunity.netinfo.NetInfoPackage" + "package": "com.horcrux.svg.SvgPackage" + }, + "ios": { + "podName": "RNSVG" + }, + "macos": { + "podName": "RNSVG" + }, + "pluginDependencies": { + "svgs": "source:rnv" }, - "androidwear": { - "package": "com.reactnativecommunity.netinfo.NetInfoPackage" + "tvos": { + "podName": "RNSVG" }, + "version": "13.7.0", "webpack": { - "modulePaths": true, "moduleAliases": { - "@react-native-community/netinfo": { - "projectPath": "node_modules/@react-native-community/netinfo/web" - } + "react-native-svg": "svgs" } } }, + "react-native-svg-charts": { + "version": "5.3.0" + }, + "react-native-swipeout": { + "version": "2.3.3" + }, + "react-native-swiper": { + "version": "github:reactseals/react-native-swiper#2.0.1" + }, "react-native-system-setting": { - "version": "1.7.2", - "ios": { - "path": "node_modules/react-native-system-setting", - "podName": "RCTSystemSetting" - }, "android": { "package": "com.ninty.system.setting.SystemSettingPackage" }, @@ -2631,390 +3669,334 @@ }, "firetv": { "package": "com.ninty.system.setting.SystemSettingPackage" - } + }, + "ios": { + "podName": "RCTSystemSetting" + }, + "version": "1.7.2" }, - "react-native-draggable-flatlist": { - "version": "1.1.7", + "react-native-tab-view": { + "version": "2.13.0", "webpack": { "modulePaths": true } }, - "@react-native-community/masked-view": { - "version": "0.1.6", + "react-native-tvos": { + "version": "0.66.3-1" + }, + "react-native-tvos-controller": { "ios": { - "path": "node_modules/@react-native-community/masked-view", - "podName": "RNCMaskedView" + "podName": "RNTVOSController" }, "tvos": { - "path": "node_modules/@react-native-community/masked-view", - "podName": "RNCMaskedView" + "podName": "RNTVOSController" }, + "version": "0.2.4" + }, + "react-native-uri-scheme": { "android": { - "path": "node_modules/@react-native-community/masked-view/android", - "projectName": "react-native-community-masked-view", - "package": "org.reactnative.maskedview.RNCMaskedViewPackage" - }, - "androidtv": { - "path": "node_modules/@react-native-community/masked-view/android", - "projectName": "react-native-community-masked-view", - "package": "org.reactnative.maskedview.RNCMaskedViewPackage" + "package": "com.rs.RNUriScheme.RNUriSchemePackage" }, - "firetv": { - "path": "node_modules/@react-native-community/masked-view/android", - "projectName": "react-native-community-masked-view", - "package": "org.reactnative.maskedview.RNCMaskedViewPackage" - } - }, - "@react-native-community/blur": { - "version": "3.6.0", "ios": { - "path": "node_modules/@react-native-community/blur", - "podName": "react-native-blur" + "podName": "RNUriScheme" }, - "tvos": { - "path": "node_modules/@react-native-community/blur", - "podName": "react-native-blur" + "version": "1.0.16" + }, + "react-native-v8": { + "android": { + "package": "io.csie.kudo.reactnative.v8.ReactNativeV8Package", + "path": "{{PLUGIN_ROOT}}/android" }, + "version": "1.6.0" + }, + "react-native-vector-icons": { "android": { - "path": "node_modules/@react-native-community/blur/android", - "projectName": "react-native-community-blur", - "package": "com.cmcewen.blurview.BlurViewPackage" + "package": "com.oblador.vectoricons.VectorIconsPackage" }, "androidtv": { - "path": "node_modules/@react-native-community/blur/android", - "projectName": "react-native-community-blur", - "package": "com.cmcewen.blurview.BlurViewPackage" + "package": "com.oblador.vectoricons.VectorIconsPackage" + }, + "androidwear": { + "package": "com.oblador.vectoricons.VectorIconsPackage" }, "firetv": { - "path": "node_modules/@react-native-community/blur/android", - "projectName": "react-native-community-blur", - "package": "com.cmcewen.blurview.BlurViewPackage" - } - }, - "react-native-fast-image": { - "version": "7.0.2", + "package": "com.oblador.vectoricons.VectorIconsPackage" + }, "ios": { - "podName": "react-native-fast-image" + "ignoreProjectFonts": [ + "FontAwesome.ttf", + "FontAwesome5_Solid.ttf", + "FontAwesome5_Regular.ttf", + "FontAwesome5_Brands.ttf", + "Feather.ttf", + "AntDesign.ttf", + "Entypo.ttf", + "EvilIcons.ttf", + "Foundation.ttf", + "Ionicons.ttf", + "MaterialCommunityIcons.ttf", + "MaterialIcons.ttf", + "Octicons.ttf", + "SimpleLineIcons.ttf", + "Zocial.ttf" + ], + "podName": "RNVectorIcons" }, "tvos": { - "podName": "react-native-fast-image" - }, - "android": { - "package": "com.dylanvann.fastimage.FastImageViewPackage" - }, - "androidtv": { - "package": "com.dylanvann.fastimage.FastImageViewPackage" - }, - "firetv": { - "package": "com.dylanvann.fastimage.FastImageViewPackage" + "ignoreProjectFonts": [ + "FontAwesome.ttf", + "FontAwesome5_Solid.ttf", + "FontAwesome5_Regular.ttf", + "FontAwesome5_Brands.ttf", + "Feather.ttf", + "AntDesign.ttf", + "Entypo.ttf", + "EvilIcons.ttf", + "Foundation.ttf", + "Ionicons.ttf", + "MaterialCommunityIcons.ttf", + "MaterialIcons.ttf", + "Octicons.ttf", + "SimpleLineIcons.ttf", + "Zocial.ttf" + ], + "podName": "RNVectorIcons" }, + "version": "9.2.0", "webpack": { + "moduleAliases": true, "modulePaths": true } }, - "react-native-webview": { - "version": "10.2.3", - "ios": { - "podName": "react-native-webview" - }, + "react-native-video": { "android": { - "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + "package": "com.brentvatne.react.ReactVideoPackage", + "path": "{{PLUGIN_ROOT}}/android-exoplayer" }, "androidtv": { - "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + "package": "com.brentvatne.react.ReactVideoPackage", + "path": "{{PLUGIN_ROOT}}/android-exoplayer" }, "firetv": { - "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + "package": "com.brentvatne.react.ReactVideoPackage", + "path": "{{PLUGIN_ROOT}}/android-exoplayer" }, - "webpack": { - "modulePaths": true - } - }, - "react-native-snap-carousel": { - "version": "3.7.5", - "webpack": { - "modulePaths": true - } - }, - "@react-native-community/slider": { - "version": "3.0.3", "ios": { - "podName": "react-native-slider" + "podName": "react-native-video" }, - "android": { - "path": "node_modules/@react-native-community/slider/android", - "package": "com.reactnativecommunity.slider.ReactSliderPackage" - } - }, - "react-native-render-html": { - "version": "4.1.1", + "macos": { + "podName": "react-native-video" + }, + "tvos": { + "podName": "react-native-video" + }, + "version": "5.2.1", "webpack": { "modulePaths": true } }, + "react-native-video-controls": { + "version": "^2.8.1" + }, "react-native-view-overflow": { - "version": "0.0.4", "android": { "package": "com.entria.views.RNViewOverflowPackage" }, + "version": "0.0.5", "webpack": { - "modulePaths": true, - "moduleAliases": true - } - }, - "detox": { - "version": "18.20.2", - "android": { - "implementation": "androidTestImplementation('com.wix:detox:+') { transitive = true }\nandroidTestImplementation 'junit:junit:4.12'", - "app/build.gradle": { - "defaultConfig": [ - "testBuildType System.getProperty('testBuildType', 'debug')", - "testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'" - ] - }, - "BuildGradle": { - "allprojects": { - "repositories": { - " maven { url \"{{resolvePackage(detox)}}/Detox-android\" }": true - } - } - } + "moduleAliases": true, + "modulePaths": true } }, - "react-native-google-cast": { - "version": "4.2.4", - "props": { - "applicationID": "CC1AD845" - }, - "ios": { - "isStatic": true, - "podName": "react-native-google-cast", - "path": "node_modules/react-native-google-cast/ios", - "appDelegateImports": [ - "GoogleCast" - ], - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - "let criteria = GCKDiscoveryCriteria(applicationID:\"{{props.applicationID}}\")", - "let options = GCKCastOptions(discoveryCriteria: criteria)", - "options.physicalVolumeButtonsWillControlDeviceVolume = true;", - "GCKCastContext.setSharedInstanceWith(options)" - ] - } - } - }, + "react-native-view-shot": { "android": { - "implementations": [ - "'com.google.android.gms:play-services-cast-framework:16.0.2'" - ], - "MainApplication": { - "packages": [ - "com.reactnative.googlecast.GoogleCastPackage" - ] - }, - "mainActivity": { - "imports": [ - "com.google.android.gms.cast.framework.CastContext" - ], - "createMethods": [ - "CastContext.getSharedInstance(this)" - ] - }, - "AndroidManifest": { - "children": [ - { - "tag": "application", - "android:name": ".MainApplication", - "children": [ - { - "tag": "meta-data", - "android:name": "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME", - "android:value": "{{configProps.id}}.CastOptionsProvider" - } - ] - } - ] - } - } - }, - "react-native-auth0": { - "version": "2.1.0", - "props": { - "DOMAIN": "" - }, - "pluginDependencies": { - "RCTLinkingIOS": "source:rnv" + "package": "fr.greweb.reactnativeviewshot.RNViewShotPackage" }, "ios": { - "podName": "A0Auth0", - "path": "node_modules/react-native-auth0/ios", - "plist": { - "CFBundleURLTypes": [ - { - "CFBundleTypeRole": "None", - "CFBundleURLName": "auth0", - "CFBundleURLSchemes": [ - "$(PRODUCT_BUNDLE_IDENTIFIER)" - ] - } - ] - } + "podName": "react-native-view-shot" }, - "android": { - "package": "com.auth0.react.A0Auth0Package", - "path": "node_modules/react-native-auth0/android", - "AndroidManifest": { - "children": [ - { - "tag": "application", - "android:name": ".MainApplication", - "children": [ - { - "tag": "activity", - "android:name": ".MainActivity", - "children": [ - { - "tag": "intent-filter", - "children": [ - { - "tag": "data", - "android:host": "{{props.DOMAIN}}", - "android:pathPrefix": "/android/${applicationId}/callback", - "android:scheme": "${applicationId}" - } - ] - } - ] - } - ] - } - ] - } - } + "version": "3.6.0" }, - "react-native-sqlite-2": { - "version": "3.0.1", - "ios": { - "path": "node_modules/react-native-sqlite-2", - "podName": "RNSqlite2" - } + "react-native-viewpager": { + "version": "0.2.13" }, - "react-native-activity-view": { - "version": "0.2.11", - "ios": { - "podName": "react-native-activity-view" + "react-native-web": { + "pluginDependencies": { + "react": "source:rnv" }, - "webpack": { - "modulePaths": true, - "moduleAliases": true - } + "version": "0.18.9" }, - "@noriginmedia/react-spatial-navigation": { - "version": "2.12.9" + "react-native-web-image-loader": { + "version": "0.0.5" }, - "react-native-view-shot": { - "version": "3.1.2", + "react-native-webrtc": { + "android": { + "package": "com.oney.WebRTCModule.WebRTCModulePackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "androidtv": { + "package": "com.oney.WebRTCModule.WebRTCModulePackage", + "path": "{{PLUGIN_ROOT}}/android" + }, + "firetv": { + "package": "com.oney.WebRTCModule.WebRTCModulePackage", + "path": "{{PLUGIN_ROOT}}/android" + }, "ios": { - "podName": "react-native-view-shot" + "path": "{{PLUGIN_ROOT}}", + "podName": "react-native-webrtc" }, + "version": "106.0.6" + }, + "react-native-webview": { "android": { - "package": "fr.greweb.reactnativeviewshot.RNViewShotPackage" + "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + }, + "androidtv": { + "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + }, + "firetv": { + "package": "com.reactnativecommunity.webview.RNCWebViewPackage" + }, + "ios": { + "podName": "react-native-webview" + }, + "macos": { + "podName": "react-native-webview" + }, + "version": "11.23.0", + "webpack": { + "modulePaths": true } }, - "react-native-splash-screen": { - "version": "3.2.0", + "react-native-webview-bridge": { + "android": { + "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" + }, + "androidtv": { + "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" + }, + "firetv": { + "package": "com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage" + }, "ios": { - "podName": "react-native-splash-screen", - "appDelegateImports": [ - "react_native_splash_screen" - ], - "appDelegateMethods": { - "application": { - "didFinishLaunchingWithOptions": [ - "RNSplashScreen.show()" - ] - } - } + "podName": "react-native-webview-bridge" }, + "version": "0.40.1" + }, + "react-native-windows": { + "version": "0.67.3" + }, + "react-native-zip-archive": { "android": { - "package": "org.devio.rn.splashscreen.SplashScreenReactPackage", - "mainActivity": { - "imports": [ - "org.devio.rn.splashscreen.SplashScreen" - ], - "createMethods": [ - "SplashScreen.show(this)" - ] - } + "package": "com.rnziparchive.RNZipArchivePackage" }, "androidtv": { - "package": "org.devio.rn.splashscreen.SplashScreenReactPackage", - "mainActivity": { - "imports": [ - "org.devio.rn.splashscreen.SplashScreen" - ], - "createMethods": [ - "SplashScreen.show(this)" - ] - } + "package": "com.rnziparchive.RNZipArchivePackage" }, "firetv": { - "package": "org.devio.rn.splashscreen.SplashScreenReactPackage", - "mainActivity": { - "imports": [ - "org.devio.rn.splashscreen.SplashScreen" - ], - "createMethods": [ - "SplashScreen.show(this)" - ] - } - } - }, - "tipsi-stripe": { - "version": "github:tipsi/tipsi-stripe#experimental", + "package": "com.rnziparchive.RNZipArchivePackage" + }, "ios": { - "podName": "tipsi-stripe" + "podName": "RNZipArchive" }, + "version": "6.0.9" + }, + "react-native-zss-rich-text-editor": { "android": { - "package": "com.gettipsi.stripe.StripeReactPackage", - "implementations": [ - "'com.google.android.gms:play-services-wallet:16.0.0'" - ], "afterEvaluate": [ - "com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true" - ] - } - }, - "react-native-iap": { - "version": "4.5.0", + "apply from: '{{PLUGIN_ROOT}}/htmlCopy.gradle';", + "copyEditorHtmlToAppAssets(file('{{PLUGIN_ROOT}}'))" + ], + "skipImplementation": true + }, "ios": { - "podName": "RNIap" + "enabled": true }, - "android": { - "package": "com.dooboolab.RNIap.RNIapPackage" + "macos": { + "enabled": true + }, + "version": "1.1.0" + }, + "react-navigation": { + "npm": { + "@react-navigation/core": "3.4.2", + "@react-navigation/native": "3.5.0", + "@react-navigation/web": "1.0.0-alpha.9" + }, + "pluginDependencies": { + "react-native-reanimated": "source:rnv", + "react-navigation-stack": "source:rnv", + "react-navigation-tabs": "source:rnv" + }, + "version": "3.9.2", + "webpack": { + "modulePaths": true } }, - "next": { - "version": "12.1.6" + "react-navigation-drawer": { + "version": "2.3.3", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } }, - "@react-native-community/cli-platform-ios": { - "version": "^6.0.0" + "react-navigation-stack": { + "version": "1.4.0", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } }, - "@react-native-community/cli-platform-android": { - "version": "^6.0.0" + "react-navigation-tabs": { + "version": "2.0.0-alpha.0", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } }, - "@react-native-community/cli": { - "version": "^6.0.0" + "react-outside-click-handler": "1.3.0", + "react-redux": { + "version": "7.2.8" + }, + "react-web-vector-icons": { + "version": "1.0.2" }, "recyclerlistview": { - "version": "3.0.0", + "version": "4.2.0", "webpack": { + "moduleAliases": true, + "modulePaths": true, "nextTranspileModules": [ "recyclerlistview" ] } }, + "renative": { + "version": "0.36.0-canary.4", + "webpack": { + "moduleAliases": true, + "modulePaths": true + } + }, + "rn-fetch-blob": { + "android": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "androidtv": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "firetv": { + "package": "com.RNFetchBlob.RNFetchBlobPackage" + }, + "ios": { + "podName": "rn-fetch-blob" + }, + "version": "0.11.2", + "webpack": { + "modulePaths": true + } + }, "rnv-platform-info": { "version": "1.0.8", "webpack": { @@ -3024,92 +4006,44 @@ } }, "Sentry": { - "no-npm": true, "ios": { "podName": "Sentry", "version": "5.2.0" - } - }, - "@sentry/react-native": { - "version": "1.7.1", - "ios": { - "podName": "RNSentry" - }, - "tvos": { - "podName": "RNSentry" - }, - "android": { - "app/build.gradle": { - "apply": [ - "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" - ] - }, - "projectName": "@sentry_react-native", - "path": "node_modules/@sentry/react-native/android", - "package": "io.sentry.RNSentryPackage", - "implementation": "implementation project(':@sentry_react-native')" - }, - "androidtv": { - "app/build.gradle": { - "apply": [ - "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" - ] - }, - "projectName": "@sentry_react-native", - "path": "node_modules/@sentry/react-native/android", - "package": "io.sentry.RNSentryPackage", - "implementation": "implementation project(':@sentry_react-native')" }, - "firetv": { - "app/build.gradle": { - "apply": [ - "from: \"{{PLUGIN_ROOT}}/sentry.gradle\"" - ] - }, - "projectName": "@sentry_react-native", - "path": "node_modules/@sentry/react-native/android", - "package": "io.sentry.RNSentryPackage", - "implementation": "implementation project(':@sentry_react-native')" + "no-npm": true + }, + "static-container": { + "version": "1.3.0", + "webpack": { + "modulePaths": true } }, - "@lightningjs/sdk": { - "version": "4.3.3" + "styled-jsx": "5.0.1", + "svgs": { + "version": "4.2.0", + "webpack": { + "moduleAliases": true + } }, - "@lightningjs/cli": { - "version": "2.7.2" + "swiper": { + "version": "6.8.4" }, - "react-native-carplay": { - "version": "2.0.0", - "ios": { - "path": "node_modules/react-native-carplay", - "podName": "react-native-carplay", - "appDelegateImports": [ - "CarPlay", - "react_native_carplay" + "theoplayer": "3.0.0", + "tipsi-stripe": { + "android": { + "afterEvaluate": [ + "com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true" ], - "appDelegateExtensions": [ - "CPApplicationDelegate" + "implementations": [ + "'com.google.android.gms:play-services-wallet:16.0.0'" ], - "appDelegateMethods": { - "application": { - "didConnectCarInterfaceController": [ - "RNCarPlay.connect(with: interfaceController, window: window)" - ], - "didDisconnectCarInterfaceController": [ - "RNCarPlay.disconnect()" - ] - } - } - } - }, - "react-native-macos": { - "version": "0.66.33" - }, - "@react-native-windows/cli": { - "version": "0.67.1" + "package": "com.gettipsi.stripe.StripeReactPackage" + }, + "ios": { + "podName": "tipsi-stripe" + }, + "version": "github:tipsi/tipsi-stripe#experimental" }, - "react-native-windows": { - "version": "0.67.3" - } + "tslib": "2.3.1" } -} +} \ No newline at end of file