diff --git a/README.md b/README.md index d89e7a5..cf48f02 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,24 @@ -# Shake for React Native +# Shake React Native SDK -[Shake](https://www.shakebugs.com) plugin for React Native. +[![npm version](https://badge.fury.io/js/@shakebugs%2Freact-native-shake.svg)](https://badge.fury.io/js/@shakebugs%2Freact-native-shake) + +React Native plugin for [bug reporting](https://www.shakebugs.com). + +## Features + +| Feature | Available | +|:-----------------:|:-----------:| +| Bug reporting | ✅ | +| Crash reporting | ❌ | +| Users | ✅ | + +## Requirements + +| Platform | Version | +|:--------------:|:---------:| +| React Native | 0.56 | +| Android | 5.0 | +| iOS | 12.0 | ## How to use @@ -26,7 +44,7 @@ Install pods from the project root directory: cd ios && pod install && cd .. ``` -## Start Shake +### Start Shake Call `Shake.start()` method in the *index.js* file. ```javascript title="index.js" @@ -42,6 +60,6 @@ Shake.start('client-id', 'client-secret'); Replace `client-id` and `client-secret` with the actual values you have in [your workspace settings](https://app.shakebugs.com/settings/workspace#general). -## Documentation +## Resources -Visit [documentation](https://www.shakebugs.com/docs) for more details. +- [Official docs](https://www.shakebugs.com/docs/) diff --git a/android/build.gradle b/android/build.gradle index 508529d..4e1b6f7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -79,7 +79,7 @@ repositories { dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules - api "$System.env.ANDROID_DEPENDENCY:15.0.+" + api "$System.env.ANDROID_DEPENDENCY:15.1.+" } def configureReactNativePom(def pom) { diff --git a/android/src/main/java/com/shakebugs/react/ShakeModule.java b/android/src/main/java/com/shakebugs/react/ShakeModule.java index 51f2928..cfb2fd6 100644 --- a/android/src/main/java/com/shakebugs/react/ShakeModule.java +++ b/android/src/main/java/com/shakebugs/react/ShakeModule.java @@ -25,8 +25,8 @@ import com.shakebugs.shake.ShakeInfo; import com.shakebugs.shake.ShakeReportConfiguration; import com.shakebugs.shake.ShakeScreen; -import com.shakebugs.shake.internal.data.NetworkRequest; -import com.shakebugs.shake.internal.data.NotificationEvent; +import com.shakebugs.shake.internal.domain.models.NetworkRequest; +import com.shakebugs.shake.internal.domain.models.NotificationEvent; import com.shakebugs.shake.privacy.NotificationEventEditor; import com.shakebugs.shake.privacy.NotificationEventsFilter; import com.shakebugs.shake.report.FeedbackType; @@ -195,6 +195,25 @@ public void run() { }); } + @ReactMethod + public void getDefaultScreen(Promise promise) { + ShakeScreen shakeScreen = Shake.getReportConfiguration().getDefaultScreen(); + String shakeScreenStr = shakeScreen.toString(); + + promise.resolve(shakeScreenStr); + } + + @ReactMethod + public void setDefaultScreen(final ReadableMap shakeScreenMap) { + runOnUiThread(new Runnable() { + @Override + public void run() { + ShakeScreen shakeScreen = mapper.mapToShakeScreen(shakeScreenMap); + Shake.getReportConfiguration().setDefaultScreen(shakeScreen); + } + }); + } + @ReactMethod public void isScreenshotIncluded(Promise promise) { promise.resolve(Shake.getReportConfiguration().isScreenshotIncluded()); @@ -355,6 +374,16 @@ public void run() { }); } + @ReactMethod + public void clearMetadata() { + runOnUiThread(new Runnable() { + @Override + public void run() { + Shake.clearMetadata(); + } + }); + } + @ReactMethod public void setShakeReportData(final ReadableArray filesArray) { runOnUiThread(new Runnable() { diff --git a/android/src/main/java/com/shakebugs/react/ShakeReflection.java b/android/src/main/java/com/shakebugs/react/ShakeReflection.java index 193ad28..a2d0a7d 100644 --- a/android/src/main/java/com/shakebugs/react/ShakeReflection.java +++ b/android/src/main/java/com/shakebugs/react/ShakeReflection.java @@ -5,8 +5,8 @@ import com.shakebugs.react.utils.Logger; import com.shakebugs.react.utils.Reflection; import com.shakebugs.shake.ShakeInfo; -import com.shakebugs.shake.internal.data.NetworkRequest; -import com.shakebugs.shake.internal.data.NotificationEvent; +import com.shakebugs.shake.internal.domain.models.NetworkRequest; +import com.shakebugs.shake.internal.domain.models.NotificationEvent; import java.lang.reflect.Method; diff --git a/android/src/main/java/com/shakebugs/react/utils/Emitter.java b/android/src/main/java/com/shakebugs/react/utils/Emitter.java index b3894db..125c613 100644 --- a/android/src/main/java/com/shakebugs/react/utils/Emitter.java +++ b/android/src/main/java/com/shakebugs/react/utils/Emitter.java @@ -3,7 +3,7 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.shakebugs.shake.internal.data.NotificationEvent; +import com.shakebugs.shake.internal.domain.models.NotificationEvent; public class Emitter { private static final String EVENT_NOTIFICATION = "EventNotification"; diff --git a/android/src/main/java/com/shakebugs/react/utils/Mapper.java b/android/src/main/java/com/shakebugs/react/utils/Mapper.java index 99c74b7..4b43faa 100644 --- a/android/src/main/java/com/shakebugs/react/utils/Mapper.java +++ b/android/src/main/java/com/shakebugs/react/utils/Mapper.java @@ -13,8 +13,8 @@ import com.shakebugs.shake.LogLevel; import com.shakebugs.shake.ShakeReportConfiguration; import com.shakebugs.shake.ShakeScreen; -import com.shakebugs.shake.internal.data.NetworkRequest; -import com.shakebugs.shake.internal.data.NotificationEvent; +import com.shakebugs.shake.internal.domain.models.NetworkRequest; +import com.shakebugs.shake.internal.domain.models.NotificationEvent; import com.shakebugs.shake.report.FeedbackType; import com.shakebugs.shake.report.ShakeFile; @@ -63,12 +63,14 @@ public ShakeReportConfiguration mapToConfiguration(ReadableMap configurationMap) boolean blackBoxData = configurationMap.getBoolean("blackBoxData"); boolean activityHistoryData = configurationMap.getBoolean("activityHistoryData"); boolean screenshot = configurationMap.getBoolean("screenshot"); + boolean video = configurationMap.getBoolean("video"); boolean showReportSentMessage = configurationMap.getBoolean("showReportSentMessage"); ShakeReportConfiguration configuration = new ShakeReportConfiguration(); configuration.blackBoxData = blackBoxData; configuration.activityHistoryData = activityHistoryData; configuration.screenshot = screenshot; + configuration.video = video; configuration.showReportSentMessage = showReportSentMessage; return configuration; @@ -233,10 +235,13 @@ private Map toMap(ReadableMap readableMap) { map.put(key, readableMap.getString(key)); break; case Map: - map.put(key, toMap(readableMap.getMap(key))); + ReadableMap m = readableMap.getMap(key); + if (m != null) map.put(key, toMap(m)); + break; case Array: - map.put(key, toArray(readableMap.getArray(key))); + ReadableArray a = readableMap.getArray(key); + if (a != null) map.put(key, toArray(a)); break; } } diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml index c586946..b82e594 100644 --- a/example/android/app/src/main/res/values/strings.xml +++ b/example/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - Shake + Shake RN diff --git a/example/ios/Podfile b/example/ios/Podfile index a2ee145..9e20e82 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -4,7 +4,7 @@ source 'https://github.com/CocoaPods/Specs.git' require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' -platform :ios, '11.0' +platform :ios, '12.0' project 'example', 'DevelopmentDebug' => :debug, @@ -15,6 +15,7 @@ project 'example', 'ProductionRelease' => :release target 'example' do + pod 'RNFS', :path => '../node_modules/react-native-fs' pod 'RNCPushNotificationIOS', :path => '../node_modules/@react-native-community/push-notification-ios' @@ -39,8 +40,5 @@ target 'example' do post_install do |installer| react_native_post_install(installer) - installer.pods_project.build_configurations.each do |config| - config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" - end end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3a5356c..43f589b 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -277,7 +277,7 @@ PODS: - React - react-native-shake (14.1.0): - React - - Shake (~> 15.0.0) + - Shake-Staging (~> 15.1.0-rc.1447) - react-native-slider (2.0.9): - React - react-native-webview (9.3.0): @@ -362,7 +362,7 @@ PODS: - React - RNScreens (2.7.0): - React - - Shake (15.0.0) + - Shake-Staging (15.1.0-rc.1447) - Yoga (1.14.0) - YogaKit (1.18.1): - Yoga (~> 1.14) @@ -435,6 +435,8 @@ DEPENDENCIES: - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: + "git@github.com:shakebugs/Specs.git": + - Shake-Staging https://github.com/CocoaPods/Specs.git: - boost-for-react-native - CocoaAsyncSocket @@ -450,7 +452,6 @@ SPEC REPOS: - fmt - libevent - OpenSSL-Universal - - Shake - YogaKit EXTERNAL SOURCES: @@ -572,7 +573,7 @@ SPEC CHECKSUMS: react-native-checkbox: 2d04aeb0c7e2c2cbd9a9a902ca6a5cf642688d09 react-native-maps: f4b89da81626ad7f151a8bfcb79733295d31ce5c react-native-safe-area-context: 8260e5157617df4b72865f44006797f895b2ada7 - react-native-shake: 91238bed6328fc1bbdefa922c1605acce7a81313 + react-native-shake: 9b00818158045abdb40f16e8ce3c039e39c9f8b4 react-native-slider: b34d943dc60deb96d952ba6b6b249aa8091e86da react-native-webview: bdde2a3c4cbdc0122bc2bfba13603db193f0cc5c React-perflogger: fd28ee1f2b5b150b00043f0301d96bd417fdc339 @@ -595,10 +596,10 @@ SPEC CHECKSUMS: RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706 - Shake: c5032f2a9dbfa2a8c33002609567f44a42176525 + Shake-Staging: 1e48bab5826b8192d7c0a3917f3e1e7d44d6b02e Yoga: aa0cb45287ebe1004c02a13f279c55a95f1572f4 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: f74bd3ae7cb43084c3aa4afe2dc911740679de66 +PODFILE CHECKSUM: 3d68bab78d41ff5c76df688461c486e9826619d6 COCOAPODS: 1.11.2 diff --git a/example/ios/example.xcodeproj/project.pbxproj b/example/ios/example.xcodeproj/project.pbxproj index b804609..1516ee3 100644 --- a/example/ios/example.xcodeproj/project.pbxproj +++ b/example/ios/example.xcodeproj/project.pbxproj @@ -8,15 +8,15 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; }; - 00FC5725332B442F5AB4C89E /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0298E4DF9CD3A91CAEF5001E /* libPods-example.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 1A842C74EED9D45D04A397D9 /* libPods-example-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 009EA06C958EA5997FE5676E /* libPods-example-exampleTests.a */; }; 41EA5607E1494A3CB79F3C92 /* HKGrotesk-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = E00A22DD20BE496093EC6617 /* HKGrotesk-Regular.otf */; }; + 754DBADED5CC5C46549BA233 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B0D9DDE07EA5E736DA1162C6 /* libPods-example.a */; }; 98B32628B0EB41388DA43CC1 /* HKGrotesk-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0AAF37C588304245AE30F947 /* HKGrotesk-SemiBold.otf */; }; EA306CE92705E55F00C98857 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA306CE82705E55F00C98857 /* File.swift */; }; EA3611B026FCCF3B009EA50C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EA3611AF26FCCF3B009EA50C /* LaunchScreen.storyboard */; }; - F4311604BB8DA34E94ACE60F /* libPods-example-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4884AC13A2FE14BC6CC0457F /* libPods-example-exampleTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -31,10 +31,10 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 009EA06C958EA5997FE5676E /* libPods-example-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = ""; }; - 0298E4DF9CD3A91CAEF5001E /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 0AAF37C588304245AE30F947 /* HKGrotesk-SemiBold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "HKGrotesk-SemiBold.otf"; path = "../src/assets/fonts/HKGrotesk-SemiBold.otf"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Shake.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shake.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; @@ -45,7 +45,6 @@ 141945E767012A3E7BC75415 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = ""; }; 27673E916AA47FB80675220E /* Pods-example.developmentdebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.developmentdebug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.developmentdebug.xcconfig"; sourceTree = ""; }; 374C326AD95B5A543FAE31C8 /* Pods-example.developmentrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.developmentrelease.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.developmentrelease.xcconfig"; sourceTree = ""; }; - 4884AC13A2FE14BC6CC0457F /* libPods-example-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5054D97E648D7D4583A8DFAF /* Pods-example-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-example-tvOSTests/Pods-example-tvOSTests.release.xcconfig"; sourceTree = ""; }; 52C48B22F0AC15C89B36F580 /* Pods-example.stagingdebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.stagingdebug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.stagingdebug.xcconfig"; sourceTree = ""; }; 6B1978008B5D8AFBF75719BA /* Pods-example.stagingrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.stagingrelease.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.stagingrelease.xcconfig"; sourceTree = ""; }; @@ -56,6 +55,7 @@ 8DC685346C6549A0378DB6BA /* Pods-example-exampleTests.developmentrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.developmentrelease.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.developmentrelease.xcconfig"; sourceTree = ""; }; 95376795FE015D0CE79F3EA2 /* Pods-example-exampleTests.developmentdebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.developmentdebug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.developmentdebug.xcconfig"; sourceTree = ""; }; A1362F1C422729666632CC26 /* Pods-example.productionrelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.productionrelease.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.productionrelease.xcconfig"; sourceTree = ""; }; + B0D9DDE07EA5E736DA1162C6 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B7295E83F979FCF39DD6E2B3 /* Pods-example.productiondebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.productiondebug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.productiondebug.xcconfig"; sourceTree = ""; }; BE240368102A5DF139E54DBA /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; E00A22DD20BE496093EC6617 /* HKGrotesk-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "HKGrotesk-Regular.otf"; path = "../src/assets/fonts/HKGrotesk-Regular.otf"; sourceTree = ""; }; @@ -76,7 +76,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F4311604BB8DA34E94ACE60F /* libPods-example-exampleTests.a in Frameworks */, + 1A842C74EED9D45D04A397D9 /* libPods-example-exampleTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -84,7 +84,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 00FC5725332B442F5AB4C89E /* libPods-example.a in Frameworks */, + 754DBADED5CC5C46549BA233 /* libPods-example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -129,8 +129,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 0298E4DF9CD3A91CAEF5001E /* libPods-example.a */, - 4884AC13A2FE14BC6CC0457F /* libPods-example-exampleTests.a */, + B0D9DDE07EA5E736DA1162C6 /* libPods-example.a */, + 009EA06C958EA5997FE5676E /* libPods-example-exampleTests.a */, ); name = Frameworks; sourceTree = ""; @@ -215,7 +215,7 @@ 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, 0AF314FDE9D4269F336FB6FE /* [CP] Embed Pods Frameworks */, - C7D46C7CD8E326D241934B5F /* [CP] Copy Pods Resources */, + 0302EC7A4707CFB3D16684C3 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -238,7 +238,7 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, FEC5104D2B973C2DC3D64DCE /* [CP] Embed Pods Frameworks */, - CF83DBBEAAE772EB50936674 /* [CP] Copy Pods Resources */, + 33D397CE537AE714E59EFE02 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -345,6 +345,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 0302EC7A4707CFB3D16684C3 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 0AF314FDE9D4269F336FB6FE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -354,7 +372,7 @@ "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Shake/Shake.framework/Shake", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Shake-Staging/Shake.framework/Shake", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -389,25 +407,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - C7D46C7CD8E326D241934B5F /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - CF83DBBEAAE772EB50936674 /* [CP] Copy Pods Resources */ = { + 33D397CE537AE714E59EFE02 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -453,7 +453,7 @@ "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Shake/Shake.framework/Shake", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Shake-Staging/Shake.framework/Shake", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -575,7 +575,7 @@ "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( @@ -682,7 +682,7 @@ CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = L32X45HDEH; INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( @@ -797,7 +797,7 @@ "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( @@ -916,7 +916,7 @@ "FB_SONARKIT_ENABLED=1", ); INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( @@ -1023,7 +1023,7 @@ CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = L32X45HDEH; INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( @@ -1126,7 +1126,7 @@ CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = L32X45HDEH; INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 15.0.0; OTHER_LDFLAGS = ( diff --git a/example/ios/example/AppDelegate.h b/example/ios/example/AppDelegate.h index ef1de86..0782e00 100644 --- a/example/ios/example/AppDelegate.h +++ b/example/ios/example/AppDelegate.h @@ -1,7 +1,9 @@ #import #import -@interface AppDelegate : UIResponder +@import UserNotifications; + +@interface AppDelegate : UIResponder @property (nonatomic, strong) UIWindow *window; diff --git a/example/ios/example/AppDelegate.m b/example/ios/example/AppDelegate.m index 1fc9d70..bc3df4d 100644 --- a/example/ios/example/AppDelegate.m +++ b/example/ios/example/AppDelegate.m @@ -12,6 +12,9 @@ #import #import +@import Shake; +@import UserNotifications; + static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; @@ -31,6 +34,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( InitializeFlipper(application); #endif + UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; + center.delegate = self; + + // Request notifications permission to enable chat notifications + [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) + completionHandler:^(BOOL granted, NSError * _Nullable error) { + }]; + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"example" @@ -50,6 +61,24 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( return YES; } +- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { + if ([response.notification.request.content.categoryIdentifier containsString:SHKNotificationCategoryIdentifierDomain]) { + [SHKShake reportNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; + return; + } + + completionHandler(); +} + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { + if ([notification.request.content.categoryIdentifier containsString:SHKNotificationCategoryIdentifierDomain]) { + [SHKShake reportNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler]; + return; + } + + completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound); +} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG diff --git a/example/ios/example/Info.plist b/example/ios/example/Info.plist index 4c1b032..4f6c52d 100644 --- a/example/ios/example/Info.plist +++ b/example/ios/example/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - example + Shake RN CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/example/src/ui/shake/MainScreen.js b/example/src/ui/shake/MainScreen.js index e9c73f2..de002eb 100644 --- a/example/src/ui/shake/MainScreen.js +++ b/example/src/ui/shake/MainScreen.js @@ -100,6 +100,7 @@ const MainScreen = props => { const configuration = new ShakeReportConfiguration(); configuration.blackBoxData = true; configuration.activityHistoryData = true; + configuration.video = true; configuration.screenshot = true; configuration.showReportSentMessage = false; @@ -123,11 +124,13 @@ const MainScreen = props => { }; const customLog = () => { - Shake.log(LogLevel.INFO, 'This is a Shake custom log.'); + Shake.log(LogLevel.INFO, 'This is Shake custom log.'); }; const addMetadata = () => { - Shake.setMetadata('Shake', 'This is a Shake metadata.'); + Shake.setMetadata('Test', 'Test metadata'); + Shake.clearMetadata(); + Shake.setMetadata('Shake', 'This is Shake metadata.'); }; const setFeedbackTypes = async () => { diff --git a/index.d.ts b/index.d.ts index 1a28aed..d3dbf4e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -31,6 +31,10 @@ declare module "react-native-shake" { export function isInvokeShakeOnScreenshot(): Promise; + export function getDefaultScreen(): Promise; + + export function setDefaultScreen(shakeScreen: ShakeScreen): void; + export function isEnableEmailField(): Promise; export function setEnableEmailField(emailFieldEnabled: boolean): void; @@ -99,6 +103,8 @@ declare module "react-native-shake" { export function setMetadata(key: string, value: string): void; + export function clearMetadata(): void; + export function addPrivateView(viewRef: object): void; export function removePrivateView(viewRef: object): void; @@ -119,6 +125,7 @@ declare module "react-native-shake" { blackBoxData: boolean; activityHistoryData: boolean; screenshot: boolean; + video: boolean showReportSentMessage: boolean; } diff --git a/index.js b/index.js index 90fab74..5871bec 100644 --- a/index.js +++ b/index.js @@ -1,520 +1,547 @@ -import { findNodeHandle, NativeModules } from "react-native"; - -import ShakeReportConfiguration from "./src/models/ShakeReportConfiguration"; -import ShakeFile from "./src/models/ShakeFile"; -import LogLevel from "./src/models/LogLevel"; -import FeedbackType from "./src/models/FeedbackType"; -import ShakeScreen from "./src/models/ShakeScreen"; -import NetworkTracker from "./src/modules/NetworkTracker"; -import NotificationTracker from "./src/modules/NotificationTracker"; -import NotificationEvent from "./src/models/NotificationEvent"; -import NetworkRequest from "./src/models/NetworkRequest"; -import NetworkRequestBuilder from "./src/builders/NetworkRequestBuilder"; -import NotificationEventBuilder from "./src/builders/NotificationEventBuilder"; - -// Export models -export { ShakeReportConfiguration }; -export { ShakeFile }; -export { LogLevel }; -export { FeedbackType }; -export { ShakeScreen }; -export { NetworkTracker }; -export { NotificationEvent }; -export { NetworkRequest }; -export { NetworkRequestBuilder }; -export { NotificationEventBuilder }; - -/** - * Interface for native methods. - */ -class Shake { - static shake = NativeModules.RNShake; - - // Helpers - static networkTracker = new NetworkTracker(this.shake); - static notificationTracker = new NotificationTracker(this.shake); - - /** - * Starts Shake SDK. - * - * @param clientId client id - * @param clientSecret client secret - */ - static start(clientId, clientSecret) { - this.shake.start(clientId, clientSecret); - this.notificationTracker.setEnabled(true); - } - - /** - * Shows shake screen. - * - * @param shakeScreen ShakeScreen.HOME or ShakeScreen.NEW - */ - static show(shakeScreen = ShakeScreen.NEW) { - this.shake.show(shakeScreen); - } - - /** - * Enables or disables Shake. - * - * @param enabled true if enabled, otherwise false - */ - static setEnabled(enabled) { - this.shake.setEnabled(enabled); - } - - /** - * Enables or disables activity history. - * - * @param enabled true if enabled, otherwise false - */ - static setEnableActivityHistory(enabled) { - this.shake.setEnableActivityHistory(enabled); - } - - /** - * Checks if activity history is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isEnableActivityHistory() { - return await this.shake.isEnableActivityHistory(); - } - - /** - * Enables or disables black box. - * - * @param enabled true if enabled, otherwise false - */ - static setEnableBlackBox(enabled) { - this.shake.setEnableBlackBox(enabled); - } - - /** - * Checks if black box is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isEnableBlackBox() { - return await this.shake.isEnableBlackBox(); - } - - /** - * Enables or disables inspect screen. - * - * @param enabled true if enabled, otherwise false - */ - static setEnableInspectScreen(enabled) { - this.shake.setEnableInspectScreen(enabled); - } - - /** - * Checks if inspect screen is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isEnableInspectScreen() { - return await this.shake.isEnableInspectScreen(); - } - - /** - * Enables or disables invoke by floating button. - * - * @param enabled true if enabled, otherwise false - */ - static setShowFloatingReportButton(enabled) { - this.shake.setShowFloatingReportButton(enabled); - } - - /** - * Checks if floating button invoke is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isShowFloatingReportButton() { - return await this.shake.isShowFloatingReportButton(); - } - - /** - * Enables or disables invoke by shake. - * - * @param enabled true if enabled, otherwise false - */ - static setInvokeShakeOnShakeDeviceEvent(enabled) { - this.shake.setInvokeShakeOnShakeDeviceEvent(enabled); - } - - /** - * Checks if shake event invoke is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isInvokeShakeOnShakeDeviceEvent() { - return await this.shake.isInvokeShakeOnShakeDeviceEvent(); - } - - /** - * Enables or disables invoke by screenshot. - * - * @param enabled true if enabled, otherwise false - */ - static setInvokeShakeOnScreenshot(enabled) { - this.shake.setInvokeShakeOnScreenshot(enabled); - } - - /** - * Checks if screenshot invoke is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isInvokeShakeOnScreenshot() { - return await this.shake.isInvokeShakeOnScreenshot(); - } - - /** - * Sets if screenshot is captured with report by default. - * - * @param screenshotIncluded true if included, otherwise false - */ - static setScreenshotIncluded(screenshotIncluded) { - this.shake.setScreenshotIncluded(screenshotIncluded); - } - - /** - * Checks if screenshot is captured with report by default. - * - * @returns {Promise<*|boolean>} true if included, otherwise false - */ - static async isScreenshotIncluded() { - return await this.shake.isScreenshotIncluded(); - } - - /** - * Sets how sensitive is shaking gesture invocation. - * - * @param shakingThreshold number between 1-1000 (1 weak, 1000 strong) - */ - static setShakingThreshold(shakingThreshold) { - this.shake.setShakingThreshold(shakingThreshold); - } - - /** - * Sets how sensitive is shaking gesture invocation. - * - * @returns {Promise<*|boolean>} shaking gesture sensitivity - */ - static async getShakingThreshold() { - return await this.shake.getShakingThreshold(); - } - - /** - * Sets files to upload with report. - * - * @param files shake files to upload - */ - static setShakeReportData(files) { - this.shake.setShakeReportData(files); - } - - /** - * Sends report silently from code. - * - * @param description silent report description - * @param files silent report files - * @param configuration silent report configuration - */ - static silentReport(description, files, configuration) { - this.shake.silentReport(description, files, configuration); - } - - /** - * Enables or disables email field on Shake screen. - * - * @param enabled true if enabled, otherwise false - */ - static setEnableEmailField(enabled) { - this.shake.setEnableEmailField(enabled); - } - - /** - * Checks if email field on Shake screen is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isEnableEmailField() { - return await this.shake.isEnableEmailField(); - } - - /** - * Sets email field value on the Shake screen. - * - * @param value email field value - */ - static setEmailField(value) { - this.shake.setEmailField(value); - } - - /** - * Gets value of the email field on the Shake screen. - * - * @returns {Promise<*|string>} email field value - */ - static async getEmailField() { - return await this.shake.getEmailField(); - } - - /** - * Checks if feedback type picker on the Shake screen is visible. - * - * @returns {Promise<*|boolean>} true if visible, otherwise false - */ - static async isFeedbackTypeEnabled() { - return await this.shake.isFeedbackTypeEnabled(); - } - - /** - * Sets if feedback type picker is visible on the Shake screen. - * - * @param enabled true if visible, otherwise false - */ - static setFeedbackTypeEnabled(enabled) { - this.shake.setFeedbackTypeEnabled(enabled); - } - - /** - * Gets ticket feedback types. - * - * @returns {Promise<*|boolean>} list of {@link FeedbackType} - */ - static async getFeedbackTypes() { - return await this.shake.getFeedbackTypes(); - } - - /** - * Sets ticket feedback types. - * - * @param feedbackTypes list of {@link FeedbackType} - */ - static setFeedbackTypes(feedbackTypes) { - this.shake.setFeedbackTypes(feedbackTypes); - } - - /** - * Checks if intro message will be shown on the first app run. - * - * @returns {Promise<*|boolean>} true if yes, otherwise false - */ - static async getShowIntroMessage() { - return await this.shake.getShowIntroMessage(); - } - - /** - * Sets if intro message will be shown on the first app run. - * - * @param enabled true if yes, otherwise false - */ - static setShowIntroMessage(enabled) { - this.shake.setShowIntroMessage(enabled); - } - - /** - * Checks if auto video recording will be enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isAutoVideoRecording() { - return await this.shake.isAutoVideoRecording(); - } - - /** - * Enables or disables auto video recording. - * - * @param enabled true if enabled, otherwise false - */ - static setAutoVideoRecording(enabled) { - this.shake.setAutoVideoRecording(enabled); - } - - /** - * Checks if console logs are attached to the report. - * - * @returns {Promise<*|boolean>} true if attached, otherwise false - */ - static async isConsoleLogsEnabled() { - return await this.shake.isConsoleLogsEnabled(); - } - - /** - * Sets if console logs are attached to the report. - * - * @param enabled true if attached, otherwise false - */ - static setConsoleLogsEnabled(enabled) { - this.shake.setConsoleLogsEnabled(enabled); - } - - /** - * Checks if network requests are attached to the report. - * - * @returns {Promise<*|boolean>} true if attached, otherwise false - */ - static async isNetworkRequestsEnabled() { - return this.networkTracker.isEnabled(); - } - - /** - * Sets if network requests are attached to the report. - * - * @param enabled true if attached, otherwise false - */ - static setNetworkRequestsEnabled(enabled) { - this.networkTracker.setEnabled(enabled); - } - - /** - * Adds custom network request to the Shake report. - * - * @param requestBuilder request builder - */ - static insertNetworkRequest(requestBuilder) { - this.networkTracker.insertNetworkRequest(requestBuilder); - } - - /** - * Adds filter for network requests. - * - * @param filter filter function - */ - static setNetworkRequestsFilter(filter) { - this.networkTracker.setFilter(filter); - } - - /** - * Adds custom notification event to the Shake report. - * - * @param notificationBuilder notification builder - */ - static insertNotificationEvent(notificationBuilder) { - this.notificationTracker.insertNotificationEvent(notificationBuilder); - } - - /** - * Adds filter for notification events. - * - * @param filter filter function - */ - static setNotificationEventsFilter(filter) { - this.notificationTracker.setFilter(filter); - } - - /** - * Logs a custom message to the report. - * - * @param logLevel LogLevel value - * @param message log message - */ - static log(logLevel, message) { - this.shake.log(logLevel, message); - } - - /** - * Adds metadata to the report. - * - * @param key metadata key - * @param value metadata value - */ - static setMetadata(key, value) { - this.shake.setMetadata(key, value); - } - - /** - * Masks view on the screenshot. - * - * @param viewRef view reference - */ - static addPrivateView(viewRef) { - const nativeTag = findNodeHandle(viewRef); - this.shake.addPrivateView(nativeTag); - } - - /** - * Removes view from private views. - * - * @param viewRef view reference - */ - static removePrivateView(viewRef) { - const nativeTag = findNodeHandle(viewRef); - this.shake.removePrivateView(nativeTag); - } - - /** - * Clears all private views. - */ - static clearPrivateViews() { - this.shake.clearPrivateViews(); - } - - /** - * Enables or disables automatic sensitive data redaction. - * - * @param enabled true if enabled, otherwise false - */ - static setSensitiveDataRedactionEnabled(enabled) { - this.shake.setSensitiveDataRedactionEnabled(enabled); - } - - /** - * Checks if automatic sensitive data redaction is enabled. - * - * @returns {Promise<*|boolean>} true if enabled, otherwise false - */ - static async isSensitiveDataRedactionEnabled() { - return await this.shake.isSensitiveDataRedactionEnabled(); - } - - /** - * Shows notifications settings screen. - * This is used just for Android os. - */ - static showNotificationsSettings() { - this.shake.showNotificationsSettings(); - } - - /** - * Registers new Shake user. - * - * @param id user id - */ - static registerUser(id) { - this.shake.registerUser(id); - } - - /** - * Updates existing Shake user id. - * - * @param id new user id - */ - static updateUserId(id) { - this.shake.updateUserId(id); - } - - /** - * Updates existing Shake user metadata. - * - * @param metadata user metadata to update - */ - static updateUserMetadata(metadata) { - this.shake.updateUserMetadata(metadata); - } - - /** - * Unregister current Shake user. - */ - static unregisterUser() { - this.shake.unregisterUser(); - } -} - -export default Shake; - - +import { findNodeHandle, NativeModules } from "react-native"; + +import ShakeReportConfiguration from "./src/models/ShakeReportConfiguration"; +import ShakeFile from "./src/models/ShakeFile"; +import LogLevel from "./src/models/LogLevel"; +import FeedbackType from "./src/models/FeedbackType"; +import ShakeScreen from "./src/models/ShakeScreen"; +import NetworkTracker from "./src/modules/NetworkTracker"; +import NotificationTracker from "./src/modules/NotificationTracker"; +import NotificationEvent from "./src/models/NotificationEvent"; +import NetworkRequest from "./src/models/NetworkRequest"; +import NetworkRequestBuilder from "./src/builders/NetworkRequestBuilder"; +import NotificationEventBuilder from "./src/builders/NotificationEventBuilder"; +import {mapToShakeScreen} from "./src/utils/Mappers"; + +// Export models +export { ShakeReportConfiguration }; +export { ShakeFile }; +export { LogLevel }; +export { FeedbackType }; +export { ShakeScreen }; +export { NetworkTracker }; +export { NotificationEvent }; +export { NetworkRequest }; +export { NetworkRequestBuilder }; +export { NotificationEventBuilder }; + +/** + * Interface for native methods. + */ +class Shake { + static shake = NativeModules.RNShake; + + // Helpers + static networkTracker = new NetworkTracker(this.shake); + static notificationTracker = new NotificationTracker(this.shake); + + /** + * Starts Shake SDK. + * + * @param clientId client id + * @param clientSecret client secret + */ + static start(clientId, clientSecret) { + this.shake.start(clientId, clientSecret); + this.notificationTracker.setEnabled(true); + } + + /** + * Shows shake screen. + * + * @param shakeScreen ShakeScreen.HOME or ShakeScreen.NEW + */ + static show(shakeScreen = ShakeScreen.NEW) { + this.shake.show(shakeScreen); + } + + /** + * Enables or disables Shake. + * + * @param enabled true if enabled, otherwise false + */ + static setEnabled(enabled) { + this.shake.setEnabled(enabled); + } + + /** + * Enables or disables activity history. + * + * @param enabled true if enabled, otherwise false + */ + static setEnableActivityHistory(enabled) { + this.shake.setEnableActivityHistory(enabled); + } + + /** + * Checks if activity history is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isEnableActivityHistory() { + return await this.shake.isEnableActivityHistory(); + } + + /** + * Enables or disables black box. + * + * @param enabled true if enabled, otherwise false + */ + static setEnableBlackBox(enabled) { + this.shake.setEnableBlackBox(enabled); + } + + /** + * Checks if black box is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isEnableBlackBox() { + return await this.shake.isEnableBlackBox(); + } + + /** + * Enables or disables inspect screen. + * + * @param enabled true if enabled, otherwise false + */ + static setEnableInspectScreen(enabled) { + this.shake.setEnableInspectScreen(enabled); + } + + /** + * Checks if inspect screen is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isEnableInspectScreen() { + return await this.shake.isEnableInspectScreen(); + } + + /** + * Enables or disables invoke by floating button. + * + * @param enabled true if enabled, otherwise false + */ + static setShowFloatingReportButton(enabled) { + this.shake.setShowFloatingReportButton(enabled); + } + + /** + * Checks if floating button invoke is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isShowFloatingReportButton() { + return await this.shake.isShowFloatingReportButton(); + } + + /** + * Enables or disables invoke by shake. + * + * @param enabled true if enabled, otherwise false + */ + static setInvokeShakeOnShakeDeviceEvent(enabled) { + this.shake.setInvokeShakeOnShakeDeviceEvent(enabled); + } + + /** + * Checks if shake event invoke is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isInvokeShakeOnShakeDeviceEvent() { + return await this.shake.isInvokeShakeOnShakeDeviceEvent(); + } + + /** + * Enables or disables invoke by screenshot. + * + * @param enabled true if enabled, otherwise false + */ + static setInvokeShakeOnScreenshot(enabled) { + this.shake.setInvokeShakeOnScreenshot(enabled); + } + + /** + * Checks if screenshot invoke is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isInvokeShakeOnScreenshot() { + return await this.shake.isInvokeShakeOnScreenshot(); + } + + /** + * Gets screen opened on Shake manual invocation. + * + * @return shake screen + */ + static async getDefaultScreen() { + const value = await this.shake.getDefaultScreen(); + return mapToShakeScreen(value); + } + + /** + * Sets screen opened on Shake manual invocation. + * + * @param shakeScreen ShakeScreen.HOME or ShakeScreen.NEW + */ + static setDefaultScreen(shakeScreen) { + this.shake.setDefaultScreen(shakeScreen); + } + + /** + * Sets if screenshot is captured with report by default. + * + * @param screenshotIncluded true if included, otherwise false + */ + static setScreenshotIncluded(screenshotIncluded) { + this.shake.setScreenshotIncluded(screenshotIncluded); + } + + /** + * Checks if screenshot is captured with report by default. + * + * @returns {Promise<*|boolean>} true if included, otherwise false + */ + static async isScreenshotIncluded() { + return await this.shake.isScreenshotIncluded(); + } + + /** + * Sets how sensitive is shaking gesture invocation. + * + * @param shakingThreshold number between 1-1000 (1 weak, 1000 strong) + */ + static setShakingThreshold(shakingThreshold) { + this.shake.setShakingThreshold(shakingThreshold); + } + + /** + * Sets how sensitive is shaking gesture invocation. + * + * @returns {Promise<*|boolean>} shaking gesture sensitivity + */ + static async getShakingThreshold() { + return await this.shake.getShakingThreshold(); + } + + /** + * Sets files to upload with report. + * + * @param files shake files to upload + */ + static setShakeReportData(files) { + this.shake.setShakeReportData(files); + } + + /** + * Sends report silently from code. + * + * @param description silent report description + * @param files silent report files + * @param configuration silent report configuration + */ + static silentReport(description, files, configuration) { + this.shake.silentReport(description, files, configuration); + } + + /** + * Enables or disables email field on Shake screen. + * + * @param enabled true if enabled, otherwise false + */ + static setEnableEmailField(enabled) { + this.shake.setEnableEmailField(enabled); + } + + /** + * Checks if email field on Shake screen is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isEnableEmailField() { + return await this.shake.isEnableEmailField(); + } + + /** + * Sets email field value on the Shake screen. + * + * @param value email field value + */ + static setEmailField(value) { + this.shake.setEmailField(value); + } + + /** + * Gets value of the email field on the Shake screen. + * + * @returns {Promise<*|string>} email field value + */ + static async getEmailField() { + return await this.shake.getEmailField(); + } + + /** + * Checks if feedback type picker on the Shake screen is visible. + * + * @returns {Promise<*|boolean>} true if visible, otherwise false + */ + static async isFeedbackTypeEnabled() { + return await this.shake.isFeedbackTypeEnabled(); + } + + /** + * Sets if feedback type picker is visible on the Shake screen. + * + * @param enabled true if visible, otherwise false + */ + static setFeedbackTypeEnabled(enabled) { + this.shake.setFeedbackTypeEnabled(enabled); + } + + /** + * Gets ticket feedback types. + * + * @returns {Promise<*|boolean>} list of {@link FeedbackType} + */ + static async getFeedbackTypes() { + return await this.shake.getFeedbackTypes(); + } + + /** + * Sets ticket feedback types. + * + * @param feedbackTypes list of {@link FeedbackType} + */ + static setFeedbackTypes(feedbackTypes) { + this.shake.setFeedbackTypes(feedbackTypes); + } + + /** + * Checks if intro message will be shown on the first app run. + * + * @returns {Promise<*|boolean>} true if yes, otherwise false + */ + static async getShowIntroMessage() { + return await this.shake.getShowIntroMessage(); + } + + /** + * Sets if intro message will be shown on the first app run. + * + * @param enabled true if yes, otherwise false + */ + static setShowIntroMessage(enabled) { + this.shake.setShowIntroMessage(enabled); + } + + /** + * Checks if auto video recording will be enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isAutoVideoRecording() { + return await this.shake.isAutoVideoRecording(); + } + + /** + * Enables or disables auto video recording. + * + * @param enabled true if enabled, otherwise false + */ + static setAutoVideoRecording(enabled) { + this.shake.setAutoVideoRecording(enabled); + } + + /** + * Checks if console logs are attached to the report. + * + * @returns {Promise<*|boolean>} true if attached, otherwise false + */ + static async isConsoleLogsEnabled() { + return await this.shake.isConsoleLogsEnabled(); + } + + /** + * Sets if console logs are attached to the report. + * + * @param enabled true if attached, otherwise false + */ + static setConsoleLogsEnabled(enabled) { + this.shake.setConsoleLogsEnabled(enabled); + } + + /** + * Checks if network requests are attached to the report. + * + * @returns {Promise<*|boolean>} true if attached, otherwise false + */ + static async isNetworkRequestsEnabled() { + return this.networkTracker.isEnabled(); + } + + /** + * Sets if network requests are attached to the report. + * + * @param enabled true if attached, otherwise false + */ + static setNetworkRequestsEnabled(enabled) { + this.networkTracker.setEnabled(enabled); + } + + /** + * Adds custom network request to the Shake report. + * + * @param requestBuilder request builder + */ + static insertNetworkRequest(requestBuilder) { + this.networkTracker.insertNetworkRequest(requestBuilder); + } + + /** + * Adds filter for network requests. + * + * @param filter filter function + */ + static setNetworkRequestsFilter(filter) { + this.networkTracker.setFilter(filter); + } + + /** + * Adds custom notification event to the Shake report. + * + * @param notificationBuilder notification builder + */ + static insertNotificationEvent(notificationBuilder) { + this.notificationTracker.insertNotificationEvent(notificationBuilder); + } + + /** + * Adds filter for notification events. + * + * @param filter filter function + */ + static setNotificationEventsFilter(filter) { + this.notificationTracker.setFilter(filter); + } + + /** + * Logs a custom message to the report. + * + * @param logLevel LogLevel value + * @param message log message + */ + static log(logLevel, message) { + this.shake.log(logLevel, message); + } + + /** + * Adds metadata to the report. + * + * @param key metadata key + * @param value metadata value + */ + static setMetadata(key, value) { + this.shake.setMetadata(key, value); + } + + /** + * Clear existing metadata. + */ + static clearMetadata() { + this.shake.clearMetadata(); + } + + /** + * Masks view on the screenshot. + * + * @param viewRef view reference + */ + static addPrivateView(viewRef) { + const nativeTag = findNodeHandle(viewRef); + this.shake.addPrivateView(nativeTag); + } + + /** + * Removes view from private views. + * + * @param viewRef view reference + */ + static removePrivateView(viewRef) { + const nativeTag = findNodeHandle(viewRef); + this.shake.removePrivateView(nativeTag); + } + + /** + * Clears all private views. + */ + static clearPrivateViews() { + this.shake.clearPrivateViews(); + } + + /** + * Enables or disables automatic sensitive data redaction. + * + * @param enabled true if enabled, otherwise false + */ + static setSensitiveDataRedactionEnabled(enabled) { + this.shake.setSensitiveDataRedactionEnabled(enabled); + } + + /** + * Checks if automatic sensitive data redaction is enabled. + * + * @returns {Promise<*|boolean>} true if enabled, otherwise false + */ + static async isSensitiveDataRedactionEnabled() { + return await this.shake.isSensitiveDataRedactionEnabled(); + } + + /** + * Shows notifications settings screen. + * This is used just for Android os. + */ + static showNotificationsSettings() { + this.shake.showNotificationsSettings(); + } + + /** + * Registers new Shake user. + * + * @param id user id + */ + static registerUser(id) { + this.shake.registerUser(id); + } + + /** + * Updates existing Shake user id. + * + * @param id new user id + */ + static updateUserId(id) { + this.shake.updateUserId(id); + } + + /** + * Updates existing Shake user metadata. + * + * @param metadata user metadata to update + */ + static updateUserMetadata(metadata) { + this.shake.updateUserMetadata(metadata); + } + + /** + * Unregister current Shake user. + */ + static unregisterUser() { + this.shake.unregisterUser(); + } +} + +export default Shake; + + diff --git a/ios/RNShake.m b/ios/RNShake.m index c4e5bd7..8e27f96 100644 --- a/ios/RNShake.m +++ b/ios/RNShake.m @@ -134,6 +134,20 @@ - (dispatch_queue_t)methodQueue resolve(isScreenshotIncluded); } +RCT_REMAP_METHOD(getDefaultScreen, getDefaultScreenwithResolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + SHKShowOption showOption = SHKShake.configuration.defaultShowOption; + NSString *showOptionStr = [self showOptionToString:showOption]; + resolve(showOptionStr); +} + +RCT_EXPORT_METHOD(setDefaultScreen:(NSDictionary*)showOptionDic) +{ + SHKShowOption showOption = [self mapToShowOption:showOptionDic]; + SHKShake.configuration.defaultShowOption = showOption; +} + RCT_EXPORT_METHOD(setShakingThreshold:(float)shakingThreshold) { SHKShake.configuration.shakingThreshold = shakingThreshold; @@ -146,7 +160,6 @@ - (dispatch_queue_t)methodQueue resolve(shakingThreshold); } - RCT_EXPORT_METHOD(setEnableEmailField:(BOOL)isEmailFieldEnabled) { SHKShake.configuration.isEmailFieldEnabled = isEmailFieldEnabled; @@ -231,6 +244,11 @@ - (dispatch_queue_t)methodQueue [SHKShake setMetadataWithKey: key value: value]; } +RCT_EXPORT_METHOD(clearMetadata) +{ + [SHKShake clearMetadata]; +} + RCT_EXPORT_METHOD(log:(NSDictionary *)logLevelDic:(NSString *)message) { if (message == nil) message = @""; @@ -377,6 +395,18 @@ - (SHKShowOption)mapToShowOption:(NSDictionary*)showOptionDic return showOption; } +- (NSString*)showOptionToString:(SHKShowOption)showOption +{ + NSString* result = @""; + + if (showOption == SHKShowOptionHome) + result = @"HOME"; + if (showOption == SHKShowOptionNew) + result = @"NEW"; + + return result; +} + - (NSMutableArray*)mapToShakeFiles:(NSArray*)files { if (files == nil) return nil; @@ -447,13 +477,15 @@ - (SHKShakeReportConfiguration*)mapToConfiguration:(NSDictionary*)configurationD BOOL includesBlackBoxData = [[configurationDic objectForKey:@"blackBoxData"] boolValue]; BOOL includesActivityHistoryData = [[configurationDic objectForKey:@"activityHistoryData"] boolValue]; - BOOL includesScreenshotImage = [[configurationDic objectForKey:@"screenshot"] boolValue]; + BOOL includesScreenshot = [[configurationDic objectForKey:@"screenshot"] boolValue]; + BOOL includesVideo = [[configurationDic objectForKey:@"video"] boolValue]; BOOL showsToastMessageOnSend = [[configurationDic objectForKey:@"showReportSentMessage"] boolValue]; SHKShakeReportConfiguration *conf = SHKShakeReportConfiguration.new; conf.includesBlackBoxData = includesBlackBoxData; conf.includesActivityHistoryData = includesActivityHistoryData; - conf.includesScreenshotImage = includesScreenshotImage; + conf.includesScreenshotImage = includesScreenshot; + conf.includesVideo = includesVideo; conf.showsToastMessageOnSend = showsToastMessageOnSend; return conf; diff --git a/react-native-shake.podspec b/react-native-shake.podspec index 0170bdc..759dead 100644 --- a/react-native-shake.podspec +++ b/react-native-shake.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| s.requires_arc = true s.dependency "React" - s.dependency "#{ENV['IOS_DEPENDENCY']}", "~> 15.0.0-rc" + s.dependency "#{ENV['IOS_DEPENDENCY']}", "~> 15.1.0-rc" # ... # s.dependency "..." end diff --git a/src/models/ShakeReportConfiguration.js b/src/models/ShakeReportConfiguration.js index c9f8638..0ef68c4 100644 --- a/src/models/ShakeReportConfiguration.js +++ b/src/models/ShakeReportConfiguration.js @@ -20,11 +20,17 @@ class ShakeReportConfiguration { */ screenshot = true; + /** + * Includes or excludes video in report. + * @type {boolean} true to include, otherwise false + */ + video = true; + /** * Defines if report success message will be visible. * @type {boolean} true if visible, otherwise false */ - showReportSentMessage = true; + showReportSentMessage = false; } export default ShakeReportConfiguration; diff --git a/src/utils/Mappers.js b/src/utils/Mappers.js new file mode 100644 index 0000000..a8361f0 --- /dev/null +++ b/src/utils/Mappers.js @@ -0,0 +1,18 @@ +import ShakeScreen from "../models/ShakeScreen"; + +/** + * Maps string to ShakeScreen enum. + * + * @param value string value + * @return shake screen + */ +const mapToShakeScreen = (value) => { + let shakeScreen; + + if (value === "NEW") shakeScreen = ShakeScreen.NEW; + if (value === "HOME") shakeScreen = ShakeScreen.HOME; + + return shakeScreen; +} + +export {mapToShakeScreen};