From 2716489de02edd1c436a33d371185dd609cd3fe1 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:50:31 +0300 Subject: [PATCH 01/20] sanity: v16.0.0 --- android/native.gradle | 2 +- examples/default/ios/Podfile.lock | 10 +++++----- ios/native.rb | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/native.gradle b/android/native.gradle index faa3246cd..f4b2e9818 100644 --- a/android/native.gradle +++ b/android/native.gradle @@ -1,5 +1,5 @@ project.ext.instabug = [ - version: '15.0.1' + version: '16.0.0' ] dependencies { diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 274dbfc09..9904b4e51 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -31,7 +31,7 @@ PODS: - hermes-engine (0.75.4): - hermes-engine/Pre-built (= 0.75.4) - hermes-engine/Pre-built (0.75.4) - - Instabug (15.1.1) + - Instabug (16.0.0) - instabug-reactnative-ndk (0.1.0): - DoubleConversion - glog @@ -1625,8 +1625,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNInstabug (15.0.2): - - Instabug (= 15.1.1) + - RNInstabug (16.0.0): + - Instabug (= 16.0.0) - React-Core - RNReanimated (3.16.1): - DoubleConversion @@ -2022,7 +2022,7 @@ SPEC CHECKSUMS: Google-Maps-iOS-Utils: f77eab4c4326d7e6a277f8e23a0232402731913a GoogleMaps: 032f676450ba0779bd8ce16840690915f84e57ac hermes-engine: ea92f60f37dba025e293cbe4b4a548fd26b610a0 - Instabug: 3e7af445c14d7823fcdecba223f09b5f7c0c6ce1 + Instabug: b891b31d33e5cd42b69aab21d32684c656bd292e instabug-reactnative-ndk: d765ac289d56e8896398d02760d9abf2562fc641 OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74 RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740 @@ -2090,7 +2090,7 @@ SPEC CHECKSUMS: ReactCommon: 6a952e50c2a4b694731d7682aaa6c79bc156e4ad RNCClipboard: 2821ac938ef46f736a8de0c8814845dde2dcbdfb RNGestureHandler: 511250b190a284388f9dd0d2e56c1df76f14cfb8 - RNInstabug: c4d26c830b40c474422012d1a216d8ea37c88151 + RNInstabug: abe8381b594fea43eddceb6f01a53a6bf380a967 RNReanimated: f42a5044d121d68e91680caacb0293f4274228eb RNScreens: c7ceced6a8384cb9be5e7a5e88e9e714401fd958 RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d diff --git a/ios/native.rb b/ios/native.rb index 41f497687..2be88af4a 100644 --- a/ios/native.rb +++ b/ios/native.rb @@ -1,4 +1,4 @@ -$instabug = { :version => '15.1.1' } +$instabug = { :version => '16.0.0' } def use_instabug! (spec = nil) version = $instabug[:version] diff --git a/package.json b/package.json index 61af51056..ed64a12c7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "instabug-reactnative", "description": "React Native plugin for integrating the Instabug SDK", - "version": "15.0.2", + "version": "16.0.0", "author": "Instabug (https://instabug.com)", "repository": "github:Instabug/Instabug-React-Native", "homepage": "https://www.instabug.com/platforms/react-native", From 5920584a97c7459fb41c5c3d74d23f81e57acbe3 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:51:50 +0300 Subject: [PATCH 02/20] [GER-7366] Support App variant (#1409) * feat: add support for app variant * feat: add support for app variant * feat: add support for app variant * feat: add support for app variant * feat: support app variant * fix: change log * fix: change log --- CHANGELOG.md | 6 ++++ .../com/instabug/reactlibrary/RNInstabug.java | 36 +++++++++++++++++-- .../RNInstabugReactnativeModule.java | 21 +++++++++++ .../RNInstabugReactnativeModuleTest.java | 12 ++++++- .../instabug/reactlibrary/RNInstabugTest.java | 9 +++-- .../ios/InstabugTests/InstabugSampleTests.m | 16 +++++++-- examples/default/src/App.tsx | 1 + ios/RNInstabug/InstabugReactBridge.h | 5 +-- ios/RNInstabug/InstabugReactBridge.m | 15 ++++++-- src/models/InstabugConfig.ts | 5 +++ src/modules/Instabug.ts | 9 +++++ src/native/NativeInstabug.ts | 2 ++ test/mocks/mockInstabug.ts | 1 + test/modules/Instabug.spec.ts | 23 ++++++++++++ 14 files changed, 148 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55cd87389..de6f7cea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v15.0.2...dev) + +### Added + +- Add support for App variant. ([#1409](https://github.com/Instabug/Instabug-React-Native/pull/1409)) + ## [15.0.2](https://github.com/Instabug/Instabug-React-Native/compare/v15.2.0...dev) ### Added diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java index 7c0901936..28de7d950 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java @@ -59,18 +59,30 @@ public void init( @NonNull Application application, @NonNull String applicationToken, int logLevel, + String codePushVersion, + String appVariant, Boolean ignoreSecureFlag, @NonNull InstabugInvocationEvent... InvocationEvent + + ) { try { setBaseUrlForDeprecationLogs(); setCurrentPlatform(); - Instabug.Builder builder = new Instabug.Builder(application, applicationToken) + Instabug.Builder builder= new Instabug.Builder(application, applicationToken) .setInvocationEvents(InvocationEvent) .setSdkDebugLogsLevel(logLevel); + if(codePushVersion!=null){ + builder.setCodePushVersion(codePushVersion); + } + if(appVariant!=null) + builder.setAppVariant(appVariant); + + + if (ignoreSecureFlag != null) { builder.ignoreFlagSecure(ignoreSecureFlag); } @@ -107,9 +119,11 @@ public void init( public void init( @NonNull Application application, @NonNull String applicationToken, + String codePushVersion, + String appVariant, @NonNull InstabugInvocationEvent... invocationEvent ) { - init(application, applicationToken, LogLevel.ERROR,null, invocationEvent); + init(application, applicationToken, LogLevel.ERROR,codePushVersion,appVariant, null,invocationEvent); } @VisibleForTesting @@ -165,6 +179,11 @@ public static class Builder { * The events that trigger the SDK's user interface. */ private InstabugInvocationEvent[] invocationEvents; + /** + * The App variant name to be used for all reports. + */ + private String appVariant; + private Boolean ignoreFlagSecure; @@ -237,6 +256,16 @@ public Builder setInvocationEvents(InstabugInvocationEvent... invocationEvents) return this; } + /** + * Sets the the current App variant + * + * @param appVariant the current App variant to work with. + */ + public Builder setAppVariant(String appVariant) { + this.appVariant = appVariant; + return this; + } + /** * Builds the Instabug instance with the provided configurations. */ @@ -252,6 +281,9 @@ public void build() { if (codePushVersion != null) { instabugBuilder.setCodePushVersion(codePushVersion); } + if(appVariant!=null){ + instabugBuilder.setAppVariant(appVariant); + } if (ignoreFlagSecure != null) { instabugBuilder.ignoreFlagSecure(ignoreFlagSecure); diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 21bcf4f44..3118bce46 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -149,6 +149,7 @@ public void init( final String logLevel, final boolean useNativeNetworkInterception, @Nullable final String codePushVersion, + @Nullable final String appVariant, final ReadableMap map ) { MainThreadHandler.runOnMainThread(new Runnable() { @@ -178,6 +179,9 @@ public void run() { builder.setCodePushVersion(codePushVersion); } } + if (appVariant != null) { + builder.setAppVariant(appVariant); + } builder.build(); } }); @@ -505,6 +509,8 @@ public void run() { }); } + + /** * Removes user attribute if exists. * @@ -1356,4 +1362,19 @@ public void run() { } }); } + + /** + * Sets current App variant + * + * @param appVariant The app variant name . + */ + @ReactMethod + public void setAppVariant(@NonNull String appVariant) { + try { + Instabug.setAppVariant(appVariant); + + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index f4f6f9bc1..9b46bf854 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -631,6 +631,16 @@ public void testRemoveAllFeatureFlags() { mockInstabug.verify(() -> Instabug.removeAllFeatureFlags()); } + @Test + public void testSetAppVariant() { + String appVariant="App-variant"; + // when + rnModule.setAppVariant(appVariant); + + // then + mockInstabug.verify(() -> Instabug.setAppVariant(appVariant)); + } + @Test public void testWillRedirectToStore() { // when @@ -678,7 +688,7 @@ public void testSetNetworkLogBodyDisabled() { mockInstabug.verify(() -> Instabug.setNetworkLogBodyEnabled(false)); } - + @Test public void testEnableAutoMasking(){ diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugTest.java index 625eab1c9..643a1e136 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugTest.java @@ -67,7 +67,7 @@ public void testInitWithLogLevel() { when(mock.setInvocationEvents(any())).thenReturn(mock); }); - sut.init(mContext, token, logLevel, true, invocationEvents); + sut.init(mContext, token, logLevel, null, null,true, invocationEvents); Instabug.Builder builder = mInstabugBuilder.constructed().get(0); @@ -89,16 +89,19 @@ public void testInitWithoutLogLevel() { final InstabugInvocationEvent[] invocationEvents = new InstabugInvocationEvent[]{InstabugInvocationEvent.FLOATING_BUTTON}; final String token = "fde...."; final int defaultLogLevel = LogLevel.ERROR; + final String appVariant = "app-variant"; MockedConstruction mInstabugBuilder = mockConstruction( Instabug.Builder.class, (mock, context) -> { when(mock.setSdkDebugLogsLevel(anyInt())).thenReturn(mock); when(mock.setInvocationEvents(any())).thenReturn(mock); + when(mock.setAppVariant(any())).thenReturn(mock); + }); - sut.init(mContext, token, invocationEvents); + sut.init(mContext, token, null, appVariant, invocationEvents); - verify(sut).init(mContext, token, defaultLogLevel, null,invocationEvents); + verify(sut).init(mContext, token, defaultLogLevel, null, appVariant, null,invocationEvents); mInstabugBuilder.close(); } diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 34fe9cfe3..7a69a6376 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -69,17 +69,19 @@ - (void)testInit { IBGInvocationEvent floatingButtonInvocationEvent = IBGInvocationEventFloatingButton; NSString *appToken = @"app_token"; NSString *codePushVersion = @"1.0.0(1)"; + NSString *appVariant = @"variant 1"; + NSArray *invocationEvents = [NSArray arrayWithObjects:[NSNumber numberWithInteger:floatingButtonInvocationEvent], nil]; BOOL useNativeNetworkInterception = YES; IBGSDKDebugLogsLevel sdkDebugLogsLevel = IBGSDKDebugLogsLevelDebug; OCMStub([mock setCodePushVersion:codePushVersion]); - [self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception codePushVersion:codePushVersion - options:nil - ]; + [self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception codePushVersion:codePushVersion appVariant:appVariant options:nil ]; OCMVerify([mock setCodePushVersion:codePushVersion]); + XCTAssertEqual(Instabug.appVariant, appVariant); + OCMVerify([self.mRNInstabug initWithToken:appToken invocationEvents:floatingButtonInvocationEvent debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception]); } @@ -101,6 +103,14 @@ - (void)testSetUserData { OCMVerify([mock setUserData:userData]); } +- (void)testSetAppVariant { + id mock = OCMClassMock([Instabug class]); + NSString *appVariant = @"appVariant"; + + [self.instabugBridge setAppVariant: appVariant]; + XCTAssertEqual(Instabug.appVariant, appVariant); +} + - (void)testSetTrackUserSteps { id mock = OCMClassMock([Instabug class]); BOOL isEnabled = true; diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index ad1c32579..82be996c5 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -49,6 +49,7 @@ export const App: React.FC = () => { invocationEvents: [InvocationEvent.floatingButton], debugLogsLevel: LogLevel.verbose, networkInterceptionMode: NetworkInterceptionMode.javascript, + appVariant: 'App variant', }); CrashReporting.setNDKCrashesEnabled(true); diff --git a/ios/RNInstabug/InstabugReactBridge.h b/ios/RNInstabug/InstabugReactBridge.h index 45c075098..bba333536 100644 --- a/ios/RNInstabug/InstabugReactBridge.h +++ b/ios/RNInstabug/InstabugReactBridge.h @@ -26,13 +26,14 @@ - (void)setEnabled:(BOOL)isEnabled; -- (void)init:(NSString *)token invocationEvents:(NSArray *)invocationEventsArray debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel useNativeNetworkInterception:(BOOL)useNativeNetworkInterception codePushVersion:(NSString *)codePushVersion -options:(nullable NSDictionary *)options; +- (void)init:(NSString *)token invocationEvents:(NSArray *)invocationEventsArray debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel useNativeNetworkInterception:(BOOL)useNativeNetworkInterception codePushVersion:(NSString *)codePushVersion appVariant:(NSString *)appVariant options:(nullable NSDictionary *)options; - (void)setCodePushVersion:(NSString *)version; - (void)setUserData:(NSString *)userData; +- (void)setAppVariant:(NSString *)appVariant; + - (void)setTrackUserSteps:(BOOL)isEnabled; - (void)setSessionProfilerEnabled:(BOOL)sessionProfilerEnabled; diff --git a/ios/RNInstabug/InstabugReactBridge.m b/ios/RNInstabug/InstabugReactBridge.m index 682896515..2291716ad 100644 --- a/ios/RNInstabug/InstabugReactBridge.m +++ b/ios/RNInstabug/InstabugReactBridge.m @@ -42,8 +42,15 @@ - (dispatch_queue_t)methodQueue { debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel useNativeNetworkInterception:(BOOL)useNativeNetworkInterception codePushVersion:(NSString *)codePushVersion - options:(nullable NSDictionary *)options - ) { + appVariant:(NSString *)appVariant + options:(nullable NSDictionary *)options + + ) { + + if(appVariant != nil){ + Instabug.appVariant = appVariant; + } + IBGInvocationEvent invocationEvents = 0; for (NSNumber *boxedValue in invocationEventsArray) { @@ -62,6 +69,10 @@ - (dispatch_queue_t)methodQueue { [Instabug setCodePushVersion:version]; } +RCT_EXPORT_METHOD(setAppVariant:(NSString *)appVariant) { + Instabug.appVariant = appVariant; +} + RCT_EXPORT_METHOD(setReproStepsConfig:(IBGUserStepsMode)bugMode :(IBGUserStepsMode)crashMode:(IBGUserStepsMode)sessionReplayMode) { [Instabug setReproStepsFor:IBGIssueTypeBug withMode:bugMode]; [Instabug setReproStepsFor:IBGIssueTypeCrash withMode:crashMode]; diff --git a/src/models/InstabugConfig.ts b/src/models/InstabugConfig.ts index af1d6e841..bc3d5c747 100644 --- a/src/models/InstabugConfig.ts +++ b/src/models/InstabugConfig.ts @@ -24,6 +24,11 @@ export interface InstabugConfig { */ ignoreAndroidSecureFlag?: boolean; + /** + * An optional current App variant to be used for filtering data. + */ + appVariant?: string; + /** * An optional network interception mode, this determines whether network interception * is done in the JavaScript side or in the native Android and iOS SDK side. diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index fd4f17600..757ac2576 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -126,6 +126,14 @@ export const init = (config: InstabugConfig) => { }, 1000); }; +/** + * Set Current App Variant. + * @param appVariant the current App variant name + */ +export const setAppVariant = (appVariant: string) => { + NativeInstabug.setAppVariant(appVariant); +}; + /** * Handles app state changes and updates APM network flags if necessary. */ @@ -273,6 +281,7 @@ const initializeNativeInstabug = (config: InstabugConfig) => { shouldEnableNativeInterception && config.networkInterceptionMode === NetworkInterceptionMode.native, config.codePushVersion, + config.appVariant, config.ignoreAndroidSecureFlag != null ? { ignoreAndroidSecureFlag: config.ignoreAndroidSecureFlag, diff --git a/src/native/NativeInstabug.ts b/src/native/NativeInstabug.ts index c9c078f37..f798bf4d2 100644 --- a/src/native/NativeInstabug.ts +++ b/src/native/NativeInstabug.ts @@ -26,6 +26,7 @@ export interface InstabugNativeModule extends NativeModule { debugLogsLevel: LogLevel, useNativeNetworkInterception: boolean, codePushVersion?: string, + appVariant?: string, options?: { ignoreAndroidSecureFlag?: boolean; }, @@ -34,6 +35,7 @@ export interface InstabugNativeModule extends NativeModule { // Misc APIs // setCodePushVersion(version: string): void; + setAppVariant(appVariant: string): void; setIBGLogPrintsToConsole(printsToConsole: boolean): void; setSessionProfilerEnabled(isEnabled: boolean): void; diff --git a/test/mocks/mockInstabug.ts b/test/mocks/mockInstabug.ts index 391a00a38..d3d5206b5 100644 --- a/test/mocks/mockInstabug.ts +++ b/test/mocks/mockInstabug.ts @@ -42,6 +42,7 @@ const mockInstabug: InstabugNativeModule = { clearAllUserAttributes: jest.fn(), showWelcomeMessageWithMode: jest.fn(), setWelcomeMessageMode: jest.fn(), + setAppVariant: jest.fn(), setFileAttachment: jest.fn(), addPrivateView: jest.fn(), removePrivateView: jest.fn(), diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index eeec8e8e4..7ec887f83 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -308,6 +308,7 @@ describe('Instabug Module', () => { instabugConfig.debugLogsLevel, usesNativeNetworkInterception, instabugConfig.codePushVersion, + undefined, { ignoreAndroidSecureFlag: instabugConfig.ignoreAndroidSecureFlag }, ); }); @@ -359,6 +360,7 @@ describe('Instabug Module', () => { // usesNativeNetworkInterception should be true when using native interception mode with iOS true, instabugConfig.codePushVersion, + undefined, { ignoreAndroidSecureFlag: instabugConfig.ignoreAndroidSecureFlag }, ); } @@ -960,6 +962,7 @@ describe('Instabug iOS initialization tests', () => { false, // Disable native interception config.codePushVersion, config.ignoreAndroidSecureFlag, + undefined, ); }); @@ -977,6 +980,7 @@ describe('Instabug iOS initialization tests', () => { true, // Enable native interception config.codePushVersion, config.ignoreAndroidSecureFlag, + undefined, ); }); @@ -994,6 +998,7 @@ describe('Instabug iOS initialization tests', () => { false, // Disable native interception config.codePushVersion, config.ignoreAndroidSecureFlag, + undefined, ); }); @@ -1036,6 +1041,7 @@ describe('Instabug Android initialization tests', () => { config.debugLogsLevel, false, // always disable native interception to insure sending network logs to core (Bugs & Crashes). config.codePushVersion, + undefined, { ignoreAndroidSecureFlag: config.ignoreAndroidSecureFlag }, ); }); @@ -1104,4 +1110,21 @@ describe('Instabug Android initialization tests', () => { ); }); }); + + it('should initialize correctly with App variant', async () => { + config.appVariant = 'App Variant'; + await Instabug.init(config); + fakeTimer(() => { + expect(NativeInstabug.setOnFeaturesUpdatedListener).toHaveBeenCalled(); + expect(NativeInstabug.init).toHaveBeenCalledWith( + config.token, + config.invocationEvents, + config.debugLogsLevel, + true, + config.codePushVersion, + config.appVariant, + undefined, + ); + }); + }); }); From 2963ad6550b2666dac22e23ce0517963ae5dc303 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:53:00 +0300 Subject: [PATCH 03/20] snapshot: fix upload sourcemap on ios (#1423) * snapshot: fix upload sourcemap on ios * snapshot: fix upload sourcemap on ios --- .../InstabugExample.xcodeproj/project.pbxproj | 2 +- ios/sourcemaps.sh | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj index fb1253b31..0778e9a59 100644 --- a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj +++ b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj @@ -430,7 +430,7 @@ name = "[CP-User] [instabug-reactnative] Upload Sourcemap"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n local source_map_file=$(generate_sourcemaps | tail -n 1)\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $source_map_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir\n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ -n \"$default_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; + shellScript = "#!/bin/sh\n\n\nexport SOURCEMAP_FILE=\"$DERIVED_FILE_DIR/main.jsbundle.map\"\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n\nlocal sourcemap_file=\"\"\n # Use existing sourcemap if available\n if [[ -f \"$SOURCEMAP_FILE\" ]]; then\n sourcemap_file=\"$SOURCEMAP_FILE\"\n else\n sourcemap_file=$(generate_sourcemaps | tail -n 1)\nfi\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\nif [ -n \"$sourcemap_file\" ]; then\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $sourcemap_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n fi\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir\n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ -n \"$default_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; }; B77A7BA143DBD17E8AAFD0B4 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/ios/sourcemaps.sh b/ios/sourcemaps.sh index 455247c4a..6b9eee7df 100644 --- a/ios/sourcemaps.sh +++ b/ios/sourcemaps.sh @@ -1,5 +1,8 @@ #!/bin/sh + +export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map" + main() { # Read environment variables from ios/.xcode.env if it exists env_path="$PODS_ROOT/../.xcode.env" @@ -28,7 +31,14 @@ main() { exit 0 fi - local source_map_file=$(generate_sourcemaps | tail -n 1) + +local sourcemap_file="" + # Use existing sourcemap if available + if [[ -f "$SOURCEMAP_FILE" ]]; then + sourcemap_file="$SOURCEMAP_FILE" + else + sourcemap_file=$(generate_sourcemaps | tail -n 1) +fi local js_project_dir="$PROJECT_DIR/.." local instabug_dir=$(dirname $(node -p "require.resolve('instabug-reactnative/package.json')")) @@ -41,12 +51,14 @@ main() { local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' "$PROJECT_DIR/$INFOPLIST_FILE") local version_code=$(resolve_var "Version Code" "INSTABUG_APP_VERSION_CODE" "$inferred_code" | tail -n 1) +if [ -n "$sourcemap_file" ]; then node $instabug_dir/bin/index.js upload-sourcemaps \ --platform ios \ - --file $source_map_file \ + --file $sourcemap_file \ --token $app_token \ --name $version_name \ --code $version_code + fi } generate_sourcemaps() { From 7698dce08577e4831d884594decfd0129f0cac90 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:11:59 +0300 Subject: [PATCH 04/20] fix: private views --- .../reactlibrary/RNInstabugReactnativeModule.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 3118bce46..45fc0a28d 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -20,10 +20,12 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.UIManagerModule; import com.instabug.apm.InternalAPM; import com.instabug.apm.configuration.cp.APMFeature; @@ -960,14 +962,22 @@ public void networkLogAndroid(final String url, @UiThread @Nullable private View resolveReactView(final int reactTag) { + try { final ReactApplicationContext reactContext = getReactApplicationContext(); final UIManagerModule uiManagerModule = reactContext.getNativeModule(UIManagerModule.class); if (uiManagerModule == null) { + UIManager uiNewManagerModule = UIManagerHelper.getUIManagerForReactTag(reactContext, reactTag); + if (uiNewManagerModule != null) { + return uiNewManagerModule.resolveView(reactTag); + } return null; } return uiManagerModule.resolveView(reactTag); + } catch (Exception e) { + return null; + } } From aa176393a7c8f713f3ca7a1b0808c55cdf21c7d2 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:13:03 +0300 Subject: [PATCH 05/20] fix: change log --- CHANGELOG.md | 5 +++++ android/sourcemaps.gradle | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de6f7cea8..906a2bb1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Add support for App variant. ([#1409](https://github.com/Instabug/Instabug-React-Native/pull/1409)) +### Fixed + +- Masking private views on newer React native Versions ([#1403](https://github.com/Instabug/Instabug-React-Native/pull/1403)) + + ## [15.0.2](https://github.com/Instabug/Instabug-React-Native/compare/v15.2.0...dev) ### Added diff --git a/android/sourcemaps.gradle b/android/sourcemaps.gradle index f544974d2..5558c01ba 100644 --- a/android/sourcemaps.gradle +++ b/android/sourcemaps.gradle @@ -1,6 +1,6 @@ import org.apache.tools.ant.taskdefs.condition.Os -gradle.projectsEvaluated { +project.afterEvaluate { // Works for both `bundleReleaseJsAndAssets` and `createBundleReleaseJsAndAssets` and product flavors def suffix = 'ReleaseJsAndAssets' def bundleTasks = project(':app').tasks.findAll { From 2d52d8d93e313ee0b3359c93ddea228382077661 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:13:03 +0300 Subject: [PATCH 06/20] fix: masking private views --- CHANGELOG.md | 5 +++++ android/sourcemaps.gradle | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de6f7cea8..906a2bb1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Add support for App variant. ([#1409](https://github.com/Instabug/Instabug-React-Native/pull/1409)) +### Fixed + +- Masking private views on newer React native Versions ([#1403](https://github.com/Instabug/Instabug-React-Native/pull/1403)) + + ## [15.0.2](https://github.com/Instabug/Instabug-React-Native/compare/v15.2.0...dev) ### Added diff --git a/android/sourcemaps.gradle b/android/sourcemaps.gradle index f544974d2..5558c01ba 100644 --- a/android/sourcemaps.gradle +++ b/android/sourcemaps.gradle @@ -1,6 +1,6 @@ import org.apache.tools.ant.taskdefs.condition.Os -gradle.projectsEvaluated { +project.afterEvaluate { // Works for both `bundleReleaseJsAndAssets` and `createBundleReleaseJsAndAssets` and product flavors def suffix = 'ReleaseJsAndAssets' def bundleTasks = project(':app').tasks.findAll { From fed6c49e366d5c1286dd167cc961787b6c9aca27 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:14:55 +0300 Subject: [PATCH 07/20] fix: masking private views --- .../instabug/reactlibrary/RNInstabugReactnativeModule.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 45fc0a28d..e28c56846 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -989,7 +989,9 @@ public void run() { try { final View view = resolveReactView(reactTag); + if(view !=null){ Instabug.addPrivateViews(view); + } } catch (Exception e) { e.printStackTrace(); } @@ -1004,8 +1006,10 @@ public void removePrivateView(final int reactTag) { public void run() { try { final View view = resolveReactView(reactTag); + if(view !=null){ Instabug.removePrivateViews(view); + } } catch (Exception e) { e.printStackTrace(); } From e45f31624026460bc4dfe8c13891cc6abcfaeb26 Mon Sep 17 00:00:00 2001 From: AyaMahmoud148 Date: Thu, 14 Aug 2025 12:11:17 +0300 Subject: [PATCH 08/20] feat: Support Advanced UI customization (#1411) * feat: Support Advanced UI customization * fix: lint * fix android test * fix: add changelog * fix: mocking file * fix: refactor redundant code * fix: refactor functions * chore: add readme file for UI customization * fix: lint * fix: rename some variables * chore: remove deperecated APIs * chore: add change log * fix: removed unused tests * fix: remove tests * fix: test failure * fix: remove deprecated apis * fix: remove tests * fix: unit test * remove commented code * fix: resolve comments * fix: unit tests * fix: setCommentMinimumCharacterCountForBugReportType * fix: ios tests * fix: private views * fix: private views * bump ios SDK to 16.0.1 --------- Co-authored-by: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Co-authored-by: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> --- CHANGELOG.md | 7 +- FONTS_SETUP_GUIDE.md | 522 ++++++++++++++++++ .../reactlibrary/RNInstabugAPMModule.java | 78 --- .../RNInstabugBugReportingModule.java | 4 +- .../RNInstabugReactnativeModule.java | 329 ++++++++--- .../reactlibrary/RNInstabugAPMModuleTest.java | 39 -- .../RNInstabugBugReportingModuleTest.java | 4 +- .../RNInstabugReactnativeModuleTest.java | 125 +++-- .../ios/InstabugTests/InstabugAPMTests.m | 47 -- .../InstabugTests/InstabugBugReportingTests.m | 12 - .../ios/InstabugTests/InstabugSampleTests.m | 106 ++-- examples/default/ios/Podfile.lock | 6 +- examples/default/react-native.config.js | 3 + examples/default/src/navigation/HomeStack.tsx | 2 - .../default/src/screens/apm/APMScreen.tsx | 1 - .../default/src/screens/apm/TracesScreen.tsx | 70 --- ios/RNInstabug/InstabugAPMBridge.h | 6 - ios/RNInstabug/InstabugAPMBridge.m | 35 -- ios/RNInstabug/InstabugBugReportingBridge.m | 8 +- ios/RNInstabug/InstabugReactBridge.h | 2 + ios/RNInstabug/InstabugReactBridge.m | 98 +++- ios/native.rb | 2 +- src/index.ts | 12 +- src/models/ThemeConfig.ts | 34 ++ src/models/Trace.ts | 33 -- src/modules/APM.ts | 24 - src/modules/BugReporting.ts | 5 +- src/modules/Instabug.ts | 88 +-- src/native/NativeAPM.ts | 4 - src/native/NativeInstabug.ts | 10 +- test/mocks/mockAPM.ts | 3 - test/mocks/mockInstabug.ts | 5 +- test/models/Trace.spec.ts | 39 -- test/modules/APM.spec.ts | 56 +- test/modules/Instabug.spec.ts | 30 +- 35 files changed, 1129 insertions(+), 720 deletions(-) create mode 100644 FONTS_SETUP_GUIDE.md create mode 100644 examples/default/react-native.config.js delete mode 100644 examples/default/src/screens/apm/TracesScreen.tsx create mode 100644 src/models/ThemeConfig.ts delete mode 100644 src/models/Trace.ts delete mode 100644 test/models/Trace.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 906a2bb1b..57b6bb5ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,16 @@ - Add support for App variant. ([#1409](https://github.com/Instabug/Instabug-React-Native/pull/1409)) +- Add Support Advanced UI customization. ([#1411](https://github.com/Instabug/Instabug-React-Native/pull/1411)) + +### Changed + +- **BREAKING** Remove deprecated APIs ([#1424](https://github.com/Instabug/Instabug-React-Native/pull/1424)). See migration guide for more details. + ### Fixed - Masking private views on newer React native Versions ([#1403](https://github.com/Instabug/Instabug-React-Native/pull/1403)) - ## [15.0.2](https://github.com/Instabug/Instabug-React-Native/compare/v15.2.0...dev) ### Added diff --git a/FONTS_SETUP_GUIDE.md b/FONTS_SETUP_GUIDE.md new file mode 100644 index 000000000..5c0c6c903 --- /dev/null +++ b/FONTS_SETUP_GUIDE.md @@ -0,0 +1,522 @@ +# Complete Font Setup Guide for Instabug React Native + +This guide covers all ways to use fonts with the `setTheme` function in Instabug React Native, including Google Fonts, custom fonts, system fonts, and both Expo and regular React Native projects. + +## Table of Contents + +1. [Overview](#overview) +2. [Font Types Supported](#font-types-supported) +3. [Regular React Native Setup](#regular-react-native-setup) +4. [Expo Setup](#expo-setup) +5. [Asset Linking Options](#asset-linking-options) +6. [Usage Examples](#usage-examples) +7. [Troubleshooting](#troubleshooting) +8. [Platform Compatibility Notes](#platform-compatibility-notes) + +## Overview + +The Instabug React Native bridge supports font loading from multiple sources: + +- **App Bundle**: Fonts included in your app assets +- **System Fonts**: Built-in platform fonts +- **Custom Fonts**: Any TTF/OTF font files +- **Google Fonts**: Downloaded TTF files from Google Fonts + +## Font Types Supported + +### 1. Google Fonts + +- Download TTF files from [Google Fonts](https://fonts.google.com/) +- Examples: Roboto, Inter, Nunito, Open Sans, Lato + +### 2. Custom Fonts + +- Any TTF/OTF font files +- Commercial fonts, free fonts, custom designs + +### 3. System Fonts + +- Platform default fonts +- No setup required +- Examples: San Francisco (iOS), Roboto (Android) + +## Regular React Native Setup + +### Method 1: Bundle Fonts (Recommended) + +#### Step 1: Download Font Files + +```bash +# Create fonts directory +mkdir fonts + +# Download your desired fonts (example with Google Fonts) +# Download from Google Fonts or any font provider +# Place TTF files in the fonts directory +``` + +#### Step 2: Add to Android + +```bash +# Create assets/fonts directory +mkdir -p android/app/src/main/assets/fonts + +# Copy font files +cp fonts/*.ttf android/app/src/main/assets/fonts/ +``` + +#### Step 3: Add to iOS + +1. **Add to Xcode Project:** + + - Open your iOS project in Xcode + - Right-click on your project → "Add Files to [ProjectName]" + - Select your TTF files + - Make sure "Add to target" is checked + +2. **Update Info.plist:** + +```xml +UIAppFonts + + Roboto-Regular.ttf + Roboto-Bold.ttf + Inter-Regular.ttf + Inter-Bold.ttf + +``` + +#### Step 4: Use with setTheme + +```typescript +import Instabug from 'instabug-reactnative'; +import { Platform } from 'react-native'; + +const applyCustomTheme = () => { + Instabug.setTheme({ + // Colors + primaryColor: '#2196F3', + backgroundColor: '#FFFFFF', + primaryTextColor: '#333333', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // Fonts - Android (use font paths) + ...(Platform.OS === 'android' && { + primaryFontPath: '/data/user/0/com.yourapp/files/fonts/Roboto-Regular.ttf', + secondaryFontPath: '/data/user/0/com.yourapp/files/fonts/Roboto-Light.ttf', + ctaFontPath: '/data/user/0/com.yourapp/files/fonts/Roboto-Bold.ttf', + }), + + // Fonts - iOS (use font paths, not assets) + ...(Platform.OS === 'ios' && { + primaryFontPath: 'fonts/Roboto-Regular.ttf', + secondaryFontPath: 'fonts/Roboto-Light.ttf', + ctaFontPath: 'fonts/Roboto-Bold.ttf', + }), + }); +}; +``` + +### Method 2: System Fonts Only + +```typescript +import Instabug from 'instabug-reactnative'; + +const applySystemTheme = () => { + Instabug.setTheme({ + // Colors only - uses system fonts + primaryColor: '#2196F3', + backgroundColor: '#FFFFFF', + primaryTextColor: '#333333', + secondaryTextColor: '#666666', + titleTextColor: '#000000', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // No font paths = uses system fonts + }); +}; +``` + +## Expo Setup + +### Method 1: Expo Fonts (Recommended for Expo) + +#### Step 1: Install Expo Fonts + +```bash +npx expo install expo-font +``` + +#### Step 2: Download and Add Fonts + +```bash +# Create fonts directory +mkdir fonts + +# Download your fonts and place them in the fonts directory +# Example: Roboto-Regular.ttf, Roboto-Bold.ttf, Inter-Regular.ttf +``` + +#### Step 3: Configure app.json + +```json +{ + "expo": { + "fonts": [ + { + "asset": "./fonts/Roboto-Regular.ttf", + "family": "Roboto-Regular" + }, + { + "asset": "./fonts/Roboto-Bold.ttf", + "family": "Roboto-Bold" + }, + { + "asset": "./fonts/Inter-Regular.ttf", + "family": "Inter-Regular" + } + ] + } +} +``` + +#### Step 4: Load Fonts in Your App + +```typescript +import * as Font from 'expo-font'; +import { useEffect, useState } from 'react'; + +export default function App() { + const [fontsLoaded, setFontsLoaded] = useState(false); + + useEffect(() => { + async function loadFonts() { + await Font.loadAsync({ + 'Roboto-Regular': require('./fonts/Roboto-Regular.ttf'), + 'Roboto-Bold': require('./fonts/Roboto-Bold.ttf'), + 'Inter-Regular': require('./fonts/Inter-Regular.ttf'), + }); + setFontsLoaded(true); + } + loadFonts(); + }, []); + + if (!fontsLoaded) { + return ; + } + + return ; +} +``` + +#### Step 5: Use with setTheme + +```typescript +import Instabug from 'instabug-reactnative'; +import { Platform } from 'react-native'; + +const applyExpoTheme = () => { + Instabug.setTheme({ + // Colors + primaryColor: '#2196F3', + backgroundColor: '#FFFFFF', + primaryTextColor: '#333333', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // Fonts - use font paths for both platforms + primaryFontPath: 'fonts/Roboto-Regular.ttf', + secondaryFontPath: 'fonts/Inter-Regular.ttf', + ctaFontPath: 'fonts/Roboto-Bold.ttf', + }); +}; +``` + +### Method 2: Expo with Bundle Fonts + +Same as Regular React Native Method 1, but fonts are automatically included in the Expo build. + +## Asset Linking Options + +### Option 1: Manual Copy (Current Method) + +- Copy TTF files to native directories +- Update Info.plist manually +- Works with all setups + +### Option 2: React Native CLI Linking + +```bash +# Create a react-native.config.js file +module.exports = { + assets: ['./fonts/'], +}; +``` + +Then run: + +```bash +npx react-native link +``` + +### Option 3: Expo Asset Linking + +```json +{ + "expo": { + "fonts": [ + { + "asset": "./fonts/Roboto-Regular.ttf", + "family": "Roboto-Regular" + } + ] + } +} +``` + +### Option 4: Metro Asset Plugin + +```bash +npm install --save-dev react-native-asset +``` + +Create `react-native.config.js`: + +```javascript +module.exports = { + assets: ['./fonts/'], +}; +``` + +## Usage Examples + +### Example 1: Google Fonts (Roboto) + +```typescript +// Download: Roboto-Regular.ttf, Roboto-Bold.ttf, Roboto-Light.ttf +// Add to project using any method above + +const applyRobotoTheme = () => { + Instabug.setTheme({ + primaryColor: '#1976D2', + backgroundColor: '#FAFAFA', + primaryTextColor: '#212121', + secondaryTextColor: '#757575', + titleTextColor: '#000000', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // Font paths for both platforms + primaryFontPath: 'fonts/Roboto-Regular.ttf', + secondaryFontPath: 'fonts/Roboto-Light.ttf', + ctaFontPath: 'fonts/Roboto-Bold.ttf', + }); +}; +``` + +### Example 2: Custom Fonts (Inter) + +```typescript +// Download: Inter-Regular.ttf, Inter-Bold.ttf, Inter-Medium.ttf +// Add to project using any method above + +const applyInterTheme = () => { + Instabug.setTheme({ + primaryColor: '#3B82F6', + backgroundColor: '#FFFFFF', + primaryTextColor: '#1F2937', + secondaryTextColor: '#6B7280', + titleTextColor: '#111827', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // Font paths for both platforms + primaryFontPath: 'fonts/Inter-Regular.ttf', + secondaryFontPath: 'fonts/Inter-Medium.ttf', + ctaFontPath: 'fonts/Inter-Bold.ttf', + }); +}; +``` + +### Example 3: System Fonts Only + +```typescript +const applySystemTheme = () => { + Instabug.setTheme({ + primaryColor: '#007AFF', + backgroundColor: '#F2F2F7', + primaryTextColor: '#000000', + secondaryTextColor: '#8E8E93', + titleTextColor: '#000000', + + // Text styles (Android only) - no font paths = uses system fonts + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + }); +}; +``` + +### Example 4: Mixed Approach + +```typescript +const applyMixedTheme = () => { + Instabug.setTheme({ + primaryColor: '#FF6B6B', + backgroundColor: '#FFFFFF', + primaryTextColor: '#2D3436', + secondaryTextColor: '#636E72', + titleTextColor: '#2D3436', + + // Text styles (Android only) + primaryTextStyle: 'normal', + secondaryTextStyle: 'normal', + ctaTextStyle: 'bold', + + // Custom font only for CTA - rest use system fonts + ctaFontPath: 'fonts/Roboto-Bold.ttf', + }); +}; +``` + +## Platform Compatibility Notes + +### **Important: iOS Font Asset Limitation** + +The iOS implementation currently **only supports** `primaryFontPath`, `secondaryFontPath`, and `ctaFontPath` properties. The `*FontAsset` properties are **not supported** on iOS. + +**Android**: Supports both `*FontPath` and `*FontAsset` properties +**iOS**: Only supports `*FontPath` properties + +### **Recommended Approach** + +Use `*FontPath` properties for both platforms to ensure compatibility: + +```typescript +// ✅ Works on both platforms +Instabug.setTheme({ + primaryFontPath: 'fonts/Roboto-Regular.ttf', + secondaryFontPath: 'fonts/Roboto-Light.ttf', + ctaFontPath: 'fonts/Roboto-Bold.ttf', +}); + +// ❌ iOS doesn't support these +Instabug.setTheme({ + primaryFontAsset: 'fonts/Roboto-Regular.ttf', // iOS ignores this + secondaryFontAsset: 'fonts/Roboto-Light.ttf', // iOS ignores this + ctaFontAsset: 'fonts/Roboto-Bold.ttf', // iOS ignores this +}); +``` + +### **Font Path Format** + +- **Android**: Can use full paths or just filenames +- **iOS**: Use relative paths like `fonts/Roboto-Regular.ttf` + +## Troubleshooting + +### Common Issues + +#### 1. Font Not Loading + +**Symptoms:** Font appears as system font or doesn't change +**Solutions:** + +- Check font filename matches exactly (case-sensitive) +- Verify font is added to both Android and iOS +- For iOS, check Info.plist entries +- For Expo, ensure fonts are loaded before using setTheme +- **iOS users**: Make sure you're using `*FontPath` properties, not `*FontAsset` + +#### 2. Font Loading in Expo + +**Symptoms:** Font works in development but not in production +**Solutions:** + +- Use `expo-font` to load fonts before app starts +- Ensure fonts are included in app.json +- Test with `expo build` or EAS Build + +#### 3. Font File Issues + +**Symptoms:** App crashes or font doesn't load +**Solutions:** + +- Verify TTF file is not corrupted +- Check file size (should be reasonable, not 0 bytes) +- Ensure font file is valid TTF/OTF format + +#### 4. Performance Issues + +**Symptoms:** App slow to start or font loading delays +**Solutions:** + +- Use system fonts for better performance +- Limit number of custom fonts +- Preload fonts in app initialization + +### Debug Steps + +1. **Check Font Loading:** + +```typescript +// Add this to debug font loading +console.log('Available fonts:', Instabug.getAvailableFonts()); // If available +``` + +2. **Verify File Paths:** + +```bash +# Check if fonts are in the right place +ls -la android/app/src/main/assets/fonts/ +ls -la ios/YourApp/ +``` + +3. **Test with System Fonts First:** + +```typescript +// Test with system fonts to ensure setTheme works +Instabug.setTheme({ + primaryColor: '#FF0000', + // No fontFamily = system fonts +}); +``` + +## Best Practices + +1. **Use System Fonts When Possible:** Better performance and consistency +2. **Limit Custom Fonts:** Use 1-2 custom fonts maximum +3. **Preload Fonts:** Load fonts before app starts +4. **Test on Both Platforms:** Fonts may behave differently on iOS vs Android +5. **Use Standard Font Weights:** Regular, Bold, Light are most reliable +6. **Keep Font Files Small:** Optimize TTF files for mobile +7. **Use \*FontPath Properties:** Ensures compatibility with both platforms + +## Summary + +- **Regular React Native:** Use bundle fonts or system fonts +- **Expo:** Use expo-font or bundle fonts +- **Asset Linking:** Available through CLI tools and Expo config +- **Google Fonts:** Download TTF files and add to project +- **Custom Fonts:** Any TTF/OTF file works the same way +- **System Fonts:** No setup required, best performance +- **Platform Compatibility:** Use `*FontPath` properties for both platforms + +The native bridge handles all font loading automatically once fonts are properly added to your project! diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java index d75b4f75b..6ed3541cb 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java @@ -12,7 +12,6 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.instabug.apm.APM; -import com.instabug.apm.model.ExecutionTrace; import com.instabug.apm.networking.APMNetworkLogger; import com.instabug.apm.networkinterception.cp.APMCPNetworkLog; import com.instabug.reactlibrary.utils.EventEmitterModule; @@ -33,8 +32,6 @@ public RNInstabugAPMModule(ReactApplicationContext reactApplicationContext) { super(reactApplicationContext); } - @Deprecated - HashMap traces = new HashMap(); @Nonnull @Override @@ -207,81 +204,6 @@ public void run() { }); } - /** - * Starts an execution trace - * - * @param name string name of the trace. - * - * @deprecated see {@link #startFlow(String)} - */ - @Deprecated - @ReactMethod - public void startExecutionTrace(final String name, final String id, final Promise promise) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - String result = ""; - ExecutionTrace trace = APM.startExecutionTrace(name); - if (trace != null) { - result = id; - traces.put(id, trace); - } - promise.resolve(result); - } catch (Exception e) { - e.printStackTrace(); - promise.resolve(null); - } - } - }); - } - - /** - * Adds a new attribute to trace - * - * @param id String id of the trace. - * @param key attribute key - * @param value attribute value. Null to remove attribute - * - * @deprecated see {@link #setFlowAttribute} - */ - @Deprecated - @ReactMethod - public void setExecutionTraceAttribute(final String id, final String key, final String value) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - traces.get(id).setAttribute(key, value); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * Ends a trace - * - * @param id string id of the trace. - * - * @deprecated see {@link #endFlow} - */ - @Deprecated - @ReactMethod - public void endExecutionTrace(final String id) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - traces.get(id).end(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - /** * Starts a UI trace * diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugBugReportingModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugBugReportingModule.java index 0dd9270e0..420011ddb 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugBugReportingModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugBugReportingModule.java @@ -389,7 +389,6 @@ public void run() { } }); } - /** * Sets a minimum number of characters as a requirement for the comments field in the different report types. * @param limit int number of characters. @@ -410,8 +409,7 @@ public void run() { typesInts[i] = types.get(i); } - BugReporting.setCommentMinimumCharacterCount(limit, typesInts); - } catch (Exception e) { + BugReporting.setCommentMinimumCharacterCountForBugReportType(limit, typesInts); } catch (Exception e) { e.printStackTrace(); } } diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index e28c56846..0dffd60c2 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -6,10 +6,14 @@ import android.app.Application; import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.util.Log; import android.view.View; +import com.facebook.react.bridge.ReactApplicationContext; + import androidx.annotation.NonNull; import androidx.annotation.UiThread; @@ -47,6 +51,7 @@ import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.logging.InstabugLog; +import com.instabug.library.model.IBGTheme; import com.instabug.library.model.NetworkLog; import com.instabug.library.model.Report; import com.instabug.library.ui.onboarding.WelcomeMessage; @@ -292,26 +297,6 @@ public void run() { }); } - /** - * Set the primary color that the SDK will use to tint certain UI elements in the SDK - * - * @param primaryColor The value of the primary color , - * whatever this color was parsed from a resource color or hex color - * or RGB color values - */ - @ReactMethod - public void setPrimaryColor(final int primaryColor) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Instabug.setPrimaryColor(primaryColor); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } /** * Gets tags. @@ -1061,60 +1046,7 @@ public void run() { }); } - /** - * @deprecated see {@link #addFeatureFlags(ReadableArray)} - */ - @ReactMethod - public void addExperiments(final ReadableArray experiments) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Object[] objectArray = ArrayUtil.toArray(experiments); - String[] stringArray = Arrays.copyOf(objectArray, objectArray.length, String[].class); - Instabug.addExperiments(Arrays.asList(stringArray)); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * @deprecated see {@link #removeFeatureFlags(ReadableArray)} - */ - @ReactMethod - public void removeExperiments(final ReadableArray experiments) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Object[] objectArray = ArrayUtil.toArray(experiments); - String[] stringArray = Arrays.copyOf(objectArray, objectArray.length, String[].class); - Instabug.removeExperiments(Arrays.asList(stringArray)); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - /** - * @deprecated see {@link #removeAllFeatureFlags()} - */ - @ReactMethod - public void clearAllExperiments() { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Instabug.clearAllExperiments(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } @ReactMethod public void addFeatureFlags(final ReadableMap featureFlagsMap) { @@ -1391,4 +1323,255 @@ public void setAppVariant(@NonNull String appVariant) { e.printStackTrace(); } } + /** + * Sets the theme for Instabug using a configuration object. + * + * @param themeConfig A ReadableMap containing theme properties such as colors, fonts, and text styles. + */ + @ReactMethod + public void setTheme(final ReadableMap themeConfig) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + com.instabug.library.model.IBGTheme.Builder builder = new com.instabug.library.model.IBGTheme.Builder(); + + // Apply colors + applyColorIfPresent(themeConfig, builder, "primaryColor", (themeBuilder, color) -> themeBuilder.setPrimaryColor(color)); + applyColorIfPresent(themeConfig, builder, "secondaryTextColor", (themeBuilder, color) -> themeBuilder.setSecondaryTextColor(color)); + applyColorIfPresent(themeConfig, builder, "primaryTextColor", (themeBuilder, color) -> themeBuilder.setPrimaryTextColor(color)); + applyColorIfPresent(themeConfig, builder, "titleTextColor", (themeBuilder, color) -> themeBuilder.setTitleTextColor(color)); + applyColorIfPresent(themeConfig, builder, "backgroundColor", (themeBuilder, color) -> themeBuilder.setBackgroundColor(color)); + + // Apply text styles + applyTextStyleIfPresent(themeConfig, builder, "primaryTextStyle", (themeBuilder, style) -> themeBuilder.setPrimaryTextStyle(style)); + applyTextStyleIfPresent(themeConfig, builder, "secondaryTextStyle", (themeBuilder, style) -> themeBuilder.setSecondaryTextStyle(style)); + applyTextStyleIfPresent(themeConfig, builder, "ctaTextStyle", (themeBuilder, style) -> themeBuilder.setCtaTextStyle(style)); + setFontIfPresent(themeConfig, builder, "primaryFontPath", "primaryFontAsset", "primary"); + setFontIfPresent(themeConfig, builder, "secondaryFontPath", "secondaryFontAsset", "secondary"); + setFontIfPresent(themeConfig, builder, "ctaFontPath", "ctaFontAsset", "CTA"); + + IBGTheme theme = builder.build(); + Instabug.setTheme(theme); + + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Retrieves a color value from the ReadableMap. + * + * @param map The ReadableMap object. + * @param key The key to look for. + * @return The parsed color as an integer, or black if missing or invalid. + */ + private int getColor(ReadableMap map, String key) { + try { + if (map != null && map.hasKey(key) && !map.isNull(key)) { + String colorString = map.getString(key); + return Color.parseColor(colorString); + } + } catch (Exception e) { + e.printStackTrace(); + } + return Color.BLACK; + } + + /** + * Retrieves a text style from the ReadableMap. + * + * @param map The ReadableMap object. + * @param key The key to look for. + * @return The corresponding Typeface style, or Typeface.NORMAL if missing or invalid. + */ + private int getTextStyle(ReadableMap map, String key) { + try { + if (map != null && map.hasKey(key) && !map.isNull(key)) { + String style = map.getString(key); + switch (style.toLowerCase()) { + case "bold": + return Typeface.BOLD; + case "italic": + return Typeface.ITALIC; + case "bold_italic": + return Typeface.BOLD_ITALIC; + case "normal": + default: + return Typeface.NORMAL; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return Typeface.NORMAL; + } + + + + /** + * Applies a color to the theme builder if present in the configuration. + * + * @param themeConfig The theme configuration map + * @param builder The theme builder + * @param key The configuration key + * @param setter The color setter function + */ + private void applyColorIfPresent(ReadableMap themeConfig, com.instabug.library.model.IBGTheme.Builder builder, + String key, java.util.function.BiConsumer setter) { + if (themeConfig.hasKey(key)) { + int color = getColor(themeConfig, key); + setter.accept(builder, color); + } + } + + /** + * Applies a text style to the theme builder if present in the configuration. + * + * @param themeConfig The theme configuration map + * @param builder The theme builder + * @param key The configuration key + * @param setter The text style setter function + */ + private void applyTextStyleIfPresent(ReadableMap themeConfig, com.instabug.library.model.IBGTheme.Builder builder, + String key, java.util.function.BiConsumer setter) { + if (themeConfig.hasKey(key)) { + int style = getTextStyle(themeConfig, key); + setter.accept(builder, style); + } + } + + /** + * Sets a font on the theme builder if the font configuration is present in the theme config. + * + * @param themeConfig The theme configuration map + * @param builder The theme builder + * @param fileKey The key for font file path + * @param assetKey The key for font asset path + * @param fontType The type of font (for logging purposes) + */ + private void setFontIfPresent(ReadableMap themeConfig, com.instabug.library.model.IBGTheme.Builder builder, + String fileKey, String assetKey, String fontType) { + if (themeConfig.hasKey(fileKey) || themeConfig.hasKey(assetKey)) { + Typeface typeface = getTypeface(themeConfig, fileKey, assetKey); + if (typeface != null) { + switch (fontType) { + case "primary": + builder.setPrimaryTextFont(typeface); + break; + case "secondary": + builder.setSecondaryTextFont(typeface); + break; + case "CTA": + builder.setCtaTextFont(typeface); + break; + } + } else { + Log.e("InstabugModule", "Failed to load " + fontType + " font"); + } + } + } + + /** + * Loads a Typeface from a file path. + * + * @param fileName The filename to load + * @return The loaded Typeface or null if failed + */ + private Typeface loadTypefaceFromFile(String fileName) { + try { + Typeface typeface = Typeface.create(fileName, Typeface.NORMAL); + if (typeface != null && !typeface.equals(Typeface.DEFAULT)) { + return typeface; + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * Loads a Typeface from assets. + * + * @param fileName The filename in assets/fonts/ directory + * @return The loaded Typeface or null if failed + */ + private Typeface loadTypefaceFromAssets(String fileName) { + try { + return Typeface.createFromAsset(getReactApplicationContext().getAssets(), "fonts/" + fileName); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private Typeface getTypeface(ReadableMap map, String fileKey, String assetKey) { + try { + if (fileKey != null && map.hasKey(fileKey) && !map.isNull(fileKey)) { + String fontPath = map.getString(fileKey); + String fileName = getFileName(fontPath); + + // Try loading from file first + Typeface typeface = loadTypefaceFromFile(fileName); + if (typeface != null) { + return typeface; + } + + // Try loading from assets + typeface = loadTypefaceFromAssets(fileName); + if (typeface != null) { + return typeface; + } + } + + if (assetKey != null && map.hasKey(assetKey) && !map.isNull(assetKey)) { + String assetPath = map.getString(assetKey); + String fileName = getFileName(assetPath); + return loadTypefaceFromAssets(fileName); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return Typeface.DEFAULT; + } + +/** + * Extracts the filename from a path, removing any directory prefixes. + * + * @param path The full path to the file + * @return Just the filename with extension + */ +private String getFileName(String path) { + if (path == null || path.isEmpty()) { + return path; + } + + int lastSeparator = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); + if (lastSeparator >= 0 && lastSeparator < path.length() - 1) { + return path.substring(lastSeparator + 1); + } + + return path; +} + + /** + * Enables or disables displaying in full-screen mode, hiding the status and navigation bars. + * @param isEnabled A boolean to enable/disable setFullscreen. + */ + @ReactMethod + public void setFullscreen(final boolean isEnabled) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Instabug.setFullscreen(isEnabled); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } } diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugAPMModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugAPMModuleTest.java index 85ca1384d..b10058f48 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugAPMModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugAPMModuleTest.java @@ -114,17 +114,6 @@ public void givenTruesetEnabled_whenQuery_thenShouldCallNativeApiWithEnabled() { APM.endAppLaunch(); } - @Test - public void givenString$startExecutionTrace_whenQuery_thenShouldCallNativeApi() { - Promise promise = mock(Promise.class); - // when - apmModule.startExecutionTrace("trace", "1", promise); - // then - verify(APM.class, times(1)); - APM.startExecutionTrace("trace"); - verify(promise).resolve(any()); - } - @Test public void testStartFlow() { String appFlowName = "appFlowName"; @@ -156,34 +145,6 @@ public void testSetFlowAttribute() { mockAPM.verifyNoMoreInteractions(); } - // @Test - // public void givenString$setExecutionTraceAttribute_whenQuery_thenShouldCallNativeApiWithIntArgs() { - // // given - // PowerMockito.mockStatic(APM.class); - // ExecutionTrace trace = mock(ExecutionTrace.class); - // Callback callback = mock(Callback.class); - // // when - // PowerMockito.whenNew(ExecutionTrace.class).withArguments("trace").thenReturn(trace); - // apmModule.startExecutionTrace("trace", "1", callback); - // apmModule.setExecutionTraceAttribute("trace", "key", "value"); - // // then - // verify(trace).setAttribute("key", "value"); - // } - - // @Test - // public void givenTrace$endExecutionTrace_whenQuery_thenShouldCallNativeApiWithIntArgs() { - // // given - // PowerMockito.mockStatic(APM.class); - // ExecutionTrace trace = mock(ExecutionTrace.class); - // Callback callback = mock(Callback.class); - // // when - // PowerMockito.whenNew(ExecutionTrace.class).withArguments("trace").thenReturn(trace); - // apmModule.startExecutionTrace("trace", "1", callback); - // apmModule.endExecutionTrace("1"); - // // then - // verify(trace).end(); - // } - @Test public void givenString$startUITrace_whenQuery_thenShouldCallNativeApiWithEnabled() { diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugBugReportingModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugBugReportingModuleTest.java index dc55e81a5..15cef0e2f 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugBugReportingModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugBugReportingModuleTest.java @@ -359,8 +359,8 @@ public Object answer(InvocationOnMock invocation) { // then verify(BugReporting.class, VerificationModeFactory.times(1)); int type1 = args.get(keysArray[0]); - - BugReporting.setCommentMinimumCharacterCount(count, type1); + + BugReporting.setCommentMinimumCharacterCountForBugReportType(count, type1); } @Test public void TestAddUserConsent() { diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index 9b46bf854..3a0edc5f0 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -193,18 +193,6 @@ public void tearDown() { Instabug.setUserData(data); } - @Test - public void givenArg$setPrimaryColor_whenQuery_thenShouldCallNativeApiWithArg() { - // given - - int color = 3902; - // when - rnModule.setPrimaryColor(color); - // then - verify(Instabug.class,times(1)); - Instabug.setPrimaryColor(color); - } - @Test public void testSetCodePushVersion() { String codePushVersion = "123"; @@ -535,51 +523,6 @@ public void testIdentifyUserWithId() { } - @Test - public void givenArg$addExperiments_whenQuery_thenShouldCallNativeApiWithArg() { - // given - JavaOnlyArray array = new JavaOnlyArray(); - array.pushString("exp1"); - array.pushString("exp2"); - - // when - rnModule.addExperiments(array); - - // then - verify(Instabug.class,times(1)); - List expectedList = new ArrayList(); - expectedList.add("exp1"); - expectedList.add("exp2"); - Instabug.addExperiments(expectedList); - } - - @Test - public void givenArg$removeExperiments_whenQuery_thenShouldCallNativeApiWithArg() { - // given - JavaOnlyArray array = new JavaOnlyArray(); - array.pushString("exp1"); - array.pushString("exp2"); - - // when - rnModule.removeExperiments(array); - - // then - verify(Instabug.class,times(1)); - List expectedList = new ArrayList(); - expectedList.add("exp1"); - expectedList.add("exp2"); - Instabug.removeExperiments(expectedList); - } - - @Test - public void given$clearAllExperiments_whenQuery_thenShouldCallNativeApi() { - // when - rnModule.clearAllExperiments(); - - // then - verify(Instabug.class,times(1)); - Instabug.clearAllExperiments(); - } @Test public void testAddFeatureFlags() { @@ -714,4 +657,72 @@ public void testGetNetworkBodyMaxSize_resolvesPromiseWithExpectedValue() { verify(promise).resolve(expected); } + @Test + public void testEnablSetFullScreen() { + boolean isEnabled = true; + + // when + rnModule.setFullscreen(isEnabled); + + // then + verify(Instabug.class, times(1)); + Instabug.setFullscreen(isEnabled); + } + + @Test + public void testDisableSetFullScreen() { + // given + boolean isEnabled = false; + + // when + rnModule.setFullscreen(isEnabled); + + // then + verify(Instabug.class, times(1)); + Instabug.setFullscreen(isEnabled); + } + + @Test + public void testSetTheme() { + // given + JavaOnlyMap themeConfig = new JavaOnlyMap(); + themeConfig.putString("primaryColor", "#FF0000"); + themeConfig.putString("primaryTextColor", "#00FF00"); + themeConfig.putString("secondaryTextColor", "#0000FF"); + themeConfig.putString("titleTextColor", "#FFFF00"); + themeConfig.putString("backgroundColor", "#FF00FF"); + themeConfig.putString("primaryTextStyle", "bold"); + themeConfig.putString("secondaryTextStyle", "italic"); + themeConfig.putString("ctaTextStyle", "bold"); + + // Mock IBGTheme.Builder + com.instabug.library.model.IBGTheme.Builder mockBuilder = mock(com.instabug.library.model.IBGTheme.Builder.class); + com.instabug.library.model.IBGTheme mockTheme = mock(com.instabug.library.model.IBGTheme.class); + + try (MockedConstruction mockedBuilder = mockConstruction( + com.instabug.library.model.IBGTheme.Builder.class, + (mock, context) -> { + when(mock.setPrimaryColor(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextColor(anyInt())).thenReturn(mock); + when(mock.setSecondaryTextColor(anyInt())).thenReturn(mock); + when(mock.setTitleTextColor(anyInt())).thenReturn(mock); + when(mock.setBackgroundColor(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextStyle(anyInt())).thenReturn(mock); + when(mock.setSecondaryTextStyle(anyInt())).thenReturn(mock); + when(mock.setCtaTextStyle(anyInt())).thenReturn(mock); + when(mock.setPrimaryTextFont(any())).thenReturn(mock); + when(mock.setSecondaryTextFont(any())).thenReturn(mock); + when(mock.setCtaTextFont(any())).thenReturn(mock); + when(mock.build()).thenReturn(mockTheme); + })) { + + // when + rnModule.setTheme(themeConfig); + + // then + verify(Instabug.class, times(1)); + Instabug.setTheme(mockTheme); + } + } + } diff --git a/examples/default/ios/InstabugTests/InstabugAPMTests.m b/examples/default/ios/InstabugTests/InstabugAPMTests.m index 949393adb..40fae6129 100644 --- a/examples/default/ios/InstabugTests/InstabugAPMTests.m +++ b/examples/default/ios/InstabugTests/InstabugAPMTests.m @@ -86,53 +86,6 @@ - (void) testSetAutoUITraceEnabled { OCMVerify([mock setAutoUITraceEnabled:isEnabled]); } -- (void) testStartExecutionTrace { - id mock = OCMClassMock([IBGAPM class]); - NSString* traceName = @"Trace_1"; - NSString* traceKey = @"1"; - RCTPromiseResolveBlock resolve = ^(id result) {}; - RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; - - OCMStub([mock startExecutionTraceWithName:traceName]); - [self.instabugBridge startExecutionTrace:traceName :traceKey :resolve :reject]; - OCMVerify([mock startExecutionTraceWithName:traceName]); -} - -- (void) testSetExecutionTraceAttribute { - NSString* traceName = @"Trace_1"; - NSString* traceId = @"Id_1"; - NSString* traceKey = @"Key_1"; - NSString* traceValue = @"1"; - RCTPromiseResolveBlock resolve = ^(id result) {}; - RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; - IBGExecutionTrace * trace = [IBGExecutionTrace alloc]; - id mock = OCMClassMock([IBGAPM class]); - id traceMock = OCMPartialMock(trace); - - OCMStub([mock startExecutionTraceWithName:traceName]).andReturn(trace); - [self.instabugBridge startExecutionTrace:traceName :traceId :resolve :reject]; - - OCMStub([traceMock setAttributeWithKey:traceKey value:traceValue]); - [self.instabugBridge setExecutionTraceAttribute:traceId :traceKey :traceValue]; - OCMVerify([traceMock setAttributeWithKey:traceKey value:traceValue]); -} - -- (void) testEndExecutionTrace { - NSString* traceName = @"Trace_1"; - NSString* traceId = @"Id_1"; - RCTPromiseResolveBlock resolve = ^(id result) {}; - RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; - IBGExecutionTrace * trace = [IBGExecutionTrace alloc]; - id apmMock = OCMClassMock([IBGAPM class]); - id traceMock = OCMPartialMock(trace); - - OCMStub([apmMock startExecutionTraceWithName:traceName]).andReturn(trace); - [self.instabugBridge startExecutionTrace:traceName :traceId :resolve :reject]; - - OCMStub([traceMock end]); - [self.instabugBridge endExecutionTrace:traceId]; - OCMVerify([traceMock end]); -} - (void) testStartFlow { id mock = OCMClassMock([IBGAPM class]); diff --git a/examples/default/ios/InstabugTests/InstabugBugReportingTests.m b/examples/default/ios/InstabugTests/InstabugBugReportingTests.m index f0b6f97ec..4d0250dc1 100644 --- a/examples/default/ios/InstabugTests/InstabugBugReportingTests.m +++ b/examples/default/ios/InstabugTests/InstabugBugReportingTests.m @@ -175,18 +175,6 @@ - (void) testSetDisclaimerText { OCMVerify([mock setDisclaimerText:text]); } -- (void) testSetCommentMinimumCharacterCount { - id mock = OCMClassMock([IBGBugReporting class]); - NSNumber *limit = [NSNumber numberWithInt:20]; - NSArray *reportTypesArr = [NSArray arrayWithObjects: @(IBGReportTypeBug), nil]; - IBGBugReportingReportType reportTypes = 0; - for (NSNumber *reportType in reportTypesArr) { - reportTypes |= [reportType intValue]; - } - OCMStub([mock setCommentMinimumCharacterCountForReportTypes:reportTypes withLimit:limit.intValue]); - [self.instabugBridge setCommentMinimumCharacterCount:limit reportTypes:reportTypesArr]; - OCMVerify([mock setCommentMinimumCharacterCountForReportTypes:reportTypes withLimit:limit.intValue]); -} - (void)testAddUserConsentWithKey { id mock = OCMClassMock([IBGBugReporting class]); diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 7a69a6376..70ea9f2de 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -153,19 +153,6 @@ - (void)testSetColorTheme { [self waitForExpectationsWithTimeout:EXPECTATION_TIMEOUT handler:nil]; } -- (void)testSetPrimaryColor { - UIColor *color = [UIColor whiteColor]; - XCTestExpectation *expectation = [self expectationWithDescription:@"Testing [Instabug setPrimaryColor]"]; - - [self.instabugBridge setPrimaryColor:color]; - [[NSRunLoop mainRunLoop] performBlock:^{ - XCTAssertEqualObjects(Instabug.tintColor, color); - [expectation fulfill]; - }]; - - [self waitForExpectationsWithTimeout:EXPECTATION_TIMEOUT handler:nil]; -} - - (void)testAppendTags { id mock = OCMClassMock([Instabug class]); NSArray *tags = @[@"tag1", @"tag2"]; @@ -251,7 +238,7 @@ - (void)testSetReproStepsConfig { [self.instabugBridge setReproStepsConfig:bugMode :crashMode :sessionReplayMode]; OCMVerify([mock setReproStepsFor:IBGIssueTypeBug withMode:bugMode]); - OCMVerify([mock setReproStepsFor:IBGIssueTypeCrash withMode:crashMode]); + OCMVerify([mock setReproStepsFor:IBGIssueTypeAllCrashes withMode:crashMode]); OCMVerify([mock setReproStepsFor:IBGIssueTypeSessionReplay withMode:sessionReplayMode]); } @@ -498,30 +485,6 @@ - (void)testClearLogs { OCMVerify([mock clearAllLogs]); } -- (void)testAddExperiments { - id mock = OCMClassMock([Instabug class]); - NSArray *experiments = @[@"exp1", @"exp2"]; - - OCMStub([mock addExperiments:experiments]); - [self.instabugBridge addExperiments:experiments]; - OCMVerify([mock addExperiments:experiments]); -} - -- (void)testRemoveExperiments { - id mock = OCMClassMock([Instabug class]); - NSArray *experiments = @[@"exp1", @"exp2"]; - - OCMStub([mock removeExperiments:experiments]); - [self.instabugBridge removeExperiments:experiments]; - OCMVerify([mock removeExperiments:experiments]); -} - -- (void)testClearAllExperiments { - id mock = OCMClassMock([Instabug class]); - OCMStub([mock clearAllExperiments]); - [self.instabugBridge clearAllExperiments]; - OCMVerify([mock clearAllExperiments]); -} - (void)testAddFeatureFlags { id mock = OCMClassMock([Instabug class]); @@ -663,6 +626,73 @@ - (void)testGetNetworkBodyMaxSize { OCMVerify(ClassMethod([mock getNetworkBodyMaxSize])); } +- (void)testSetTheme { + id mock = OCMClassMock([Instabug class]); + id mockTheme = OCMClassMock([IBGTheme class]); + + // Create theme configuration dictionary + NSDictionary *themeConfig = @{ + @"primaryColor": @"#FF0000", + @"backgroundColor": @"#00FF00", + @"titleTextColor": @"#0000FF", + @"subtitleTextColor": @"#FFFF00", + @"primaryTextColor": @"#FF00FF", + @"secondaryTextColor": @"#00FFFF", + @"callToActionTextColor": @"#800080", + @"headerBackgroundColor": @"#808080", + @"footerBackgroundColor": @"#C0C0C0", + @"rowBackgroundColor": @"#FFFFFF", + @"selectedRowBackgroundColor": @"#E6E6FA", + @"rowSeparatorColor": @"#D3D3D3", + @"primaryFontPath": @"TestFont.ttf", + @"secondaryFontPath": @"fonts/AnotherFont.ttf", + @"ctaFontPath": @"./assets/fonts/CTAFont.ttf" + }; + + // Mock IBGTheme creation and configuration + OCMStub([mockTheme primaryColor]).andReturn([UIColor redColor]); + OCMStub([mockTheme backgroundColor]).andReturn([UIColor greenColor]); + OCMStub([mockTheme titleTextColor]).andReturn([UIColor blueColor]); + OCMStub([mockTheme subtitleTextColor]).andReturn([UIColor yellowColor]); + OCMStub([mockTheme primaryTextColor]).andReturn([UIColor magentaColor]); + OCMStub([mockTheme secondaryTextColor]).andReturn([UIColor cyanColor]); + OCMStub([mockTheme callToActionTextColor]).andReturn([UIColor purpleColor]); + OCMStub([mockTheme headerBackgroundColor]).andReturn([UIColor grayColor]); + OCMStub([mockTheme footerBackgroundColor]).andReturn([UIColor lightGrayColor]); + OCMStub([mockTheme rowBackgroundColor]).andReturn([UIColor whiteColor]); + OCMStub([mockTheme selectedRowBackgroundColor]).andReturn([UIColor redColor]); + OCMStub([mockTheme rowSeparatorColor]).andReturn([UIColor lightGrayColor]); + OCMStub([mockTheme primaryTextFont]).andReturn([UIFont systemFontOfSize:17.0]); + OCMStub([mockTheme secondaryTextFont]).andReturn([UIFont systemFontOfSize:17.0]); + OCMStub([mockTheme callToActionTextFont]).andReturn([UIFont systemFontOfSize:17.0]); + + // Mock theme property setting + OCMStub([mockTheme setPrimaryColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setBackgroundColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setTitleTextColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setSubtitleTextColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setPrimaryTextColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setSecondaryTextColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setCallToActionTextColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setHeaderBackgroundColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setFooterBackgroundColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setRowBackgroundColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setSelectedRowBackgroundColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setRowSeparatorColor:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setPrimaryTextFont:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setSecondaryTextFont:[OCMArg any]]).andReturn(mockTheme); + OCMStub([mockTheme setCallToActionTextFont:[OCMArg any]]).andReturn(mockTheme); + + // Mock Instabug.theme property + OCMStub([mock theme]).andReturn(mockTheme); + OCMStub([mock setTheme:[OCMArg any]]); + + // Call the method + [self.instabugBridge setTheme:themeConfig]; + + // Verify that setTheme was called + OCMVerify([mock setTheme:[OCMArg any]]); +} @end diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 9904b4e51..d9f1e9e13 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -31,7 +31,7 @@ PODS: - hermes-engine (0.75.4): - hermes-engine/Pre-built (= 0.75.4) - hermes-engine/Pre-built (0.75.4) - - Instabug (16.0.0) + - Instabug (16.0.1) - instabug-reactnative-ndk (0.1.0): - DoubleConversion - glog @@ -1626,7 +1626,7 @@ PODS: - ReactCommon/turbomodule/core - Yoga - RNInstabug (16.0.0): - - Instabug (= 16.0.0) + - Instabug (= 16.0.1) - React-Core - RNReanimated (3.16.1): - DoubleConversion @@ -2022,7 +2022,7 @@ SPEC CHECKSUMS: Google-Maps-iOS-Utils: f77eab4c4326d7e6a277f8e23a0232402731913a GoogleMaps: 032f676450ba0779bd8ce16840690915f84e57ac hermes-engine: ea92f60f37dba025e293cbe4b4a548fd26b610a0 - Instabug: b891b31d33e5cd42b69aab21d32684c656bd292e + Instabug: f31a27e70cb6580aef656d1abf8c57a5bb5633d0 instabug-reactnative-ndk: d765ac289d56e8896398d02760d9abf2562fc641 OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74 RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740 diff --git a/examples/default/react-native.config.js b/examples/default/react-native.config.js new file mode 100644 index 000000000..cbdf34c94 --- /dev/null +++ b/examples/default/react-native.config.js @@ -0,0 +1,3 @@ +module.exports = { + assets: ['./assets/fonts/'], +}; diff --git a/examples/default/src/navigation/HomeStack.tsx b/examples/default/src/navigation/HomeStack.tsx index 090aa6587..f3ebbf79b 100644 --- a/examples/default/src/navigation/HomeStack.tsx +++ b/examples/default/src/navigation/HomeStack.tsx @@ -22,7 +22,6 @@ import { import { GoogleMapsScreen } from '../screens/user-steps/GoogleMapsScreen'; import { LargeImageListScreen } from '../screens/user-steps/LargeImageListScreen'; import { APMScreen } from '../screens/apm/APMScreen'; -import { TracesScreen } from '../screens/apm/TracesScreen'; import { NetworkScreen } from '../screens/apm/NetworkScreen'; import { FlowsScreen } from '../screens/apm/FlowsScreen'; import { SessionReplayScreen } from '../screens/SessionReplayScreen'; @@ -140,7 +139,6 @@ export const HomeStackNavigator: React.FC = () => { - APM.endAppLaunch()} /> navigation.navigate('NetworkTraces')} /> - navigation.navigate('ExecutionTraces')} /> navigation.navigate('AppFlows')} /> navigation.navigate('WebViews')} /> navigation.navigate('ComplexViews')} /> diff --git a/examples/default/src/screens/apm/TracesScreen.tsx b/examples/default/src/screens/apm/TracesScreen.tsx deleted file mode 100644 index bd3e41838..000000000 --- a/examples/default/src/screens/apm/TracesScreen.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useState } from 'react'; -import { APM, Trace } from 'instabug-reactnative'; -import { ScrollView } from 'react-native'; -import { Section } from '../../components/Section'; -import { Screen } from '../../components/Screen'; -import { VStack } from 'native-base'; -import { InputField } from '../../components/InputField'; -import { CustomButton } from '../../components/CustomButton'; -import BackgroundTimer from 'react-native-background-timer'; - -export const TracesScreen: React.FC = () => { - const [traceName, setTraceName] = useState(''); - const [traceAttributeKey, setTraceAttributeKey] = useState(''); - const [traceAttributeValue, setTraceAttributeValue] = useState(''); - let executionTrace: Trace; - - async function startTrace() { - executionTrace = await APM.startExecutionTrace(traceName ?? ''); - } - - async function startDelayedTrace() { - return BackgroundTimer.setTimeout(async () => { - executionTrace = await APM.startExecutionTrace(traceName ?? ''); - }, 5000); - } - - function setTraceAttribute() { - if (!executionTrace) { - console.log('Please, start a trace before setting attributes.'); - } - return executionTrace.setAttribute(traceAttributeKey ?? '', traceAttributeValue ?? ''); - } - - function endExecutionTrace() { - if (!executionTrace) { - console.log('Please, start a trace before ending it.'); - } - return executionTrace.end(); - } - - return ( - - -
- - setTraceName(text)} - value={traceName} - /> - - - setTraceAttributeKey(text)} - value={traceAttributeKey} - /> - setTraceAttributeValue(text)} - value={traceAttributeValue} - /> - - - -
-
-
- ); -}; diff --git a/ios/RNInstabug/InstabugAPMBridge.h b/ios/RNInstabug/InstabugAPMBridge.h index 0a0ea397c..6b09dba6b 100644 --- a/ios/RNInstabug/InstabugAPMBridge.h +++ b/ios/RNInstabug/InstabugAPMBridge.h @@ -15,12 +15,6 @@ - (void)setAppLaunchEnabled:(BOOL)isEnabled; - (void)endAppLaunch; - (void)setAutoUITraceEnabled:(BOOL)isEnabled; -- (void)startExecutionTrace:(NSString *)name :(NSString *)id - :(RCTPromiseResolveBlock)resolve - :(RCTPromiseRejectBlock)reject DEPRECATED_MSG_ATTRIBUTE("Please use APM.startFlow instead."); -- (void)setExecutionTraceAttribute:(NSString *)id:(NSString *)key - :(NSString *)value DEPRECATED_MSG_ATTRIBUTE("Please use APM.setTraceAttribute instead."); -- (void)endExecutionTrace:(NSString *)id DEPRECATED_MSG_ATTRIBUTE("Please use APM.endFlow instead."); - (void)startFlow:(NSString *)name; - (void)endFlow:(NSString *)name; - (void)setFlowAttribute:(NSString *)name :(NSString *)key :(NSString *_Nullable)value; diff --git a/ios/RNInstabug/InstabugAPMBridge.m b/ios/RNInstabug/InstabugAPMBridge.m index c28c7f425..29319264d 100644 --- a/ios/RNInstabug/InstabugAPMBridge.m +++ b/ios/RNInstabug/InstabugAPMBridge.m @@ -61,41 +61,6 @@ - (id) init IBGAPM.autoUITraceEnabled = isEnabled; } -// Starts new execution trace with the specified `name`. -// -// Deprecated see [startFlow: (NSString *)name] -RCT_EXPORT_METHOD(startExecutionTrace:(NSString *)name :(NSString *)id - :(RCTPromiseResolveBlock)resolve - :(RCTPromiseRejectBlock)reject) { - IBGExecutionTrace *trace = [IBGAPM startExecutionTraceWithName:name]; - if (trace != nil) { - [traces setObject: trace forKey: id]; - resolve(id); - } else { - resolve([NSNull null]); - } -} - -// Sets a user defined attribute for the execution trace. -// -// Deprecated see [setFlowAttribute:(NSString *)name :(NSString *)key :(NSString *_Nullable)value] -RCT_EXPORT_METHOD(setExecutionTraceAttribute:(NSString *)id :(NSString *)key :(NSString *)value) { - IBGExecutionTrace *trace = [traces objectForKey:id]; - if (trace != nil) { - [trace setAttributeWithKey:key value:value]; - } -} - -// Ends execution trace with the specified `name`. -// -// Deprecated see [endFlow: (NSString *)name] -RCT_EXPORT_METHOD(endExecutionTrace:(NSString *)id) { - IBGExecutionTrace *trace = [traces objectForKey:id]; - if (trace != nil) { - [trace end]; - } -} - // Starts a flow trace with the specified `name`, // allowing the SDK to capture and analyze the flow of execution within the application. RCT_EXPORT_METHOD(startFlow: (NSString *)name) { diff --git a/ios/RNInstabug/InstabugBugReportingBridge.m b/ios/RNInstabug/InstabugBugReportingBridge.m index 75e058eb7..70efaa129 100644 --- a/ios/RNInstabug/InstabugBugReportingBridge.m +++ b/ios/RNInstabug/InstabugBugReportingBridge.m @@ -205,18 +205,16 @@ - (void) showBugReportingWithReportTypeAndOptionsHelper:(NSArray*)args { } RCT_EXPORT_METHOD(setCommentMinimumCharacterCount:(nonnull NSNumber *)limit reportTypes:(NSArray *)reportTypes) { - IBGBugReportingReportType parsedReportTypes = 0; - + IBGBugReportingType parsedReportTypes = 0; if (![reportTypes count]) { - parsedReportTypes = @(IBGBugReportingReportTypeBug).integerValue | @(IBGBugReportingReportTypeFeedback).integerValue | @(IBGBugReportingReportTypeQuestion).integerValue; + parsedReportTypes = @(IBGBugReportingTypeBug).integerValue | @(IBGBugReportingTypeFeedback).integerValue | @(IBGBugReportingTypeQuestion).integerValue; } else { for (NSNumber *reportType in reportTypes) { parsedReportTypes |= [reportType intValue]; } } - - [IBGBugReporting setCommentMinimumCharacterCountForReportTypes:parsedReportTypes withLimit:limit.intValue]; + [IBGBugReporting setCommentMinimumCharacterCount:[limit integerValue] forBugReportType:parsedReportTypes]; } RCT_EXPORT_METHOD(addUserConsent:(NSString *)key diff --git a/ios/RNInstabug/InstabugReactBridge.h b/ios/RNInstabug/InstabugReactBridge.h index bba333536..187caa6c6 100644 --- a/ios/RNInstabug/InstabugReactBridge.h +++ b/ios/RNInstabug/InstabugReactBridge.h @@ -44,6 +44,8 @@ - (void)setPrimaryColor:(UIColor *)color; +- (void)setTheme:(NSDictionary *)themeConfig; + - (void)appendTags:(NSArray *)tags; - (void)resetTags; diff --git a/ios/RNInstabug/InstabugReactBridge.m b/ios/RNInstabug/InstabugReactBridge.m index 2291716ad..fe9e11e8a 100644 --- a/ios/RNInstabug/InstabugReactBridge.m +++ b/ios/RNInstabug/InstabugReactBridge.m @@ -75,8 +75,8 @@ - (dispatch_queue_t)methodQueue { RCT_EXPORT_METHOD(setReproStepsConfig:(IBGUserStepsMode)bugMode :(IBGUserStepsMode)crashMode:(IBGUserStepsMode)sessionReplayMode) { [Instabug setReproStepsFor:IBGIssueTypeBug withMode:bugMode]; - [Instabug setReproStepsFor:IBGIssueTypeCrash withMode:crashMode]; - [Instabug setReproStepsFor:IBGIssueTypeSessionReplay withMode:sessionReplayMode]; + [Instabug setReproStepsFor:IBGIssueTypeAllCrashes withMode:crashMode]; + [Instabug setReproStepsFor:IBGIssueTypeSessionReplay withMode:sessionReplayMode]; } RCT_EXPORT_METHOD(setFileAttachment:(NSString *)fileLocation) { @@ -182,10 +182,88 @@ - (dispatch_queue_t)methodQueue { [Instabug setColorTheme:colorTheme]; } -RCT_EXPORT_METHOD(setPrimaryColor:(UIColor *)color) { - Instabug.tintColor = color; + +RCT_EXPORT_METHOD(setTheme:(NSDictionary *)themeConfig) { + IBGTheme *theme = [[IBGTheme alloc] init]; + + NSDictionary *colorMapping = @{ + @"primaryColor": ^(UIColor *color) { theme.primaryColor = color; }, + @"backgroundColor": ^(UIColor *color) { theme.backgroundColor = color; }, + @"titleTextColor": ^(UIColor *color) { theme.titleTextColor = color; }, + @"subtitleTextColor": ^(UIColor *color) { theme.subtitleTextColor = color; }, + @"primaryTextColor": ^(UIColor *color) { theme.primaryTextColor = color; }, + @"secondaryTextColor": ^(UIColor *color) { theme.secondaryTextColor = color; }, + @"callToActionTextColor": ^(UIColor *color) { theme.callToActionTextColor = color; }, + @"headerBackgroundColor": ^(UIColor *color) { theme.headerBackgroundColor = color; }, + @"footerBackgroundColor": ^(UIColor *color) { theme.footerBackgroundColor = color; }, + @"rowBackgroundColor": ^(UIColor *color) { theme.rowBackgroundColor = color; }, + @"selectedRowBackgroundColor": ^(UIColor *color) { theme.selectedRowBackgroundColor = color; }, + @"rowSeparatorColor": ^(UIColor *color) { theme.rowSeparatorColor = color; } + }; + + for (NSString *key in colorMapping) { + if (themeConfig[key]) { + NSString *colorString = themeConfig[key]; + UIColor *color = [self colorFromHexString:colorString]; + if (color) { + void (^setter)(UIColor *) = colorMapping[key]; + setter(color); + } + } + } + + [self setFontIfPresent:themeConfig[@"primaryFontPath"] forTheme:theme type:@"primary"]; + [self setFontIfPresent:themeConfig[@"secondaryFontPath"] forTheme:theme type:@"secondary"]; + [self setFontIfPresent:themeConfig[@"ctaFontPath"] forTheme:theme type:@"cta"]; + + Instabug.theme = theme; +} + +- (void)setFontIfPresent:(NSString *)fontPath forTheme:(IBGTheme *)theme type:(NSString *)type { + if (fontPath) { + NSString *fileName = [fontPath lastPathComponent]; + NSString *nameWithoutExtension = [fileName stringByDeletingPathExtension]; + UIFont *font = [UIFont fontWithName:nameWithoutExtension size:17.0]; + if (font) { + if ([type isEqualToString:@"primary"]) { + theme.primaryTextFont = font; + } else if ([type isEqualToString:@"secondary"]) { + theme.secondaryTextFont = font; + } else if ([type isEqualToString:@"cta"]) { + theme.callToActionTextFont = font; + } + } + } } +- (UIColor *)colorFromHexString:(NSString *)hexString { + NSString *cleanString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; + + if (cleanString.length == 6) { + unsigned int rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:cleanString]; + [scanner scanHexInt:&rgbValue]; + + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0 + green:((rgbValue & 0xFF00) >> 8) / 255.0 + blue:(rgbValue & 0xFF) / 255.0 + alpha:1.0]; + } else if (cleanString.length == 8) { + unsigned int rgbaValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:cleanString]; + [scanner scanHexInt:&rgbaValue]; + + return [UIColor colorWithRed:((rgbaValue & 0xFF000000) >> 24) / 255.0 + green:((rgbaValue & 0xFF0000) >> 16) / 255.0 + blue:((rgbaValue & 0xFF00) >> 8) / 255.0 + alpha:(rgbaValue & 0xFF) / 255.0]; + } + + return [UIColor blackColor]; +} + + + RCT_EXPORT_METHOD(appendTags:(NSArray *)tags) { [Instabug appendTags:tags]; } @@ -368,18 +446,6 @@ - (dispatch_queue_t)methodQueue { } } -RCT_EXPORT_METHOD(addExperiments:(NSArray *)experiments) { - [Instabug addExperiments:experiments]; -} - -RCT_EXPORT_METHOD(removeExperiments:(NSArray *)experiments) { - [Instabug removeExperiments:experiments]; -} - -RCT_EXPORT_METHOD(clearAllExperiments) { - [Instabug clearAllExperiments]; -} - RCT_EXPORT_METHOD(addFeatureFlags:(NSDictionary *)featureFlagsMap) { NSMutableArray *featureFlags = [NSMutableArray array]; for(id key in featureFlagsMap){ diff --git a/ios/native.rb b/ios/native.rb index 2be88af4a..6c1848522 100644 --- a/ios/native.rb +++ b/ios/native.rb @@ -1,4 +1,4 @@ -$instabug = { :version => '16.0.0' } +$instabug = { :version => '16.0.1' } def use_instabug! (spec = nil) version = $instabug[:version] diff --git a/src/index.ts b/src/index.ts index 6e7de0284..0dcb8cafa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ // Models import type { InstabugConfig } from './models/InstabugConfig'; import Report from './models/Report'; -import Trace from './models/Trace'; +import type { ThemeConfig } from './models/ThemeConfig'; // Modules import * as APM from './modules/APM'; import * as BugReporting from './modules/BugReporting'; @@ -19,7 +19,6 @@ import type { SessionMetadata } from './models/SessionMetadata'; export * from './utils/Enums'; export { Report, - Trace, APM, BugReporting, CrashReporting, @@ -29,6 +28,13 @@ export { Replies, Surveys, }; -export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, SessionMetadata }; +export type { + InstabugConfig, + Survey, + NetworkData, + NetworkDataObfuscationHandler, + SessionMetadata, + ThemeConfig, +}; export default Instabug; diff --git a/src/models/ThemeConfig.ts b/src/models/ThemeConfig.ts new file mode 100644 index 000000000..fb90347c9 --- /dev/null +++ b/src/models/ThemeConfig.ts @@ -0,0 +1,34 @@ +export type ThemeConfig = { + // Colors + primaryColor?: string; + backgroundColor?: string; + titleTextColor?: string; + subtitleTextColor?: string; + primaryTextColor?: string; + secondaryTextColor?: string; + callToActionTextColor?: string; + headerBackgroundColor?: string; + footerBackgroundColor?: string; + rowBackgroundColor?: string; + selectedRowBackgroundColor?: string; + rowSeparatorColor?: string; + + // Text Styles (Android only) + primaryTextStyle?: 'bold' | 'italic' | 'normal'; + secondaryTextStyle?: 'bold' | 'italic' | 'normal'; + titleTextStyle?: 'bold' | 'italic' | 'normal'; + ctaTextStyle?: 'bold' | 'italic' | 'normal'; + + // Fonts + primaryFontPath?: string; + primaryFontAsset?: string; + secondaryFontPath?: string; + secondaryFontAsset?: string; + ctaFontPath?: string; + ctaFontAsset?: string; + + // Legacy properties (deprecated) + primaryTextType?: string; + secondaryTextType?: string; + ctaTextType?: string; +}; diff --git a/src/models/Trace.ts b/src/models/Trace.ts deleted file mode 100644 index 19cd26d58..000000000 --- a/src/models/Trace.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NativeAPM } from '../native/NativeAPM'; -import type * as APM from '../modules/APM'; - -export default class Trace { - constructor( - public readonly id: string, - public readonly name: string = '', - public readonly attributes: Record = {}, - ) {} - - /** - * Adds an attribute with a specified key and value to the Trace to be sent. - * - * @param key - The key of the attribute. - * @param value - The value of the attribute. - * - * @deprecated Please migrate to the App Flows APIs: {@link APM.startFlow}, {@link APM.endFlow}, and {@link APM.setFlowAttribute}. - */ - setAttribute(key: string, value: string) { - NativeAPM.setExecutionTraceAttribute(this.id, key, value); - this.attributes[key] = value; - } - - /** - * Ends the execution trace. - * - * @deprecated Please migrate to the App Flows APIs: {@link APM.startFlow}, {@link APM.endFlow}, and {@link APM.setFlowAttribute}. - */ - - end() { - NativeAPM.endExecutionTrace(this.id); - } -} diff --git a/src/modules/APM.ts b/src/modules/APM.ts index 92d401389..9f2dcbe01 100644 --- a/src/modules/APM.ts +++ b/src/modules/APM.ts @@ -1,6 +1,5 @@ import { Platform } from 'react-native'; -import Trace from '../models/Trace'; import { NativeAPM } from '../native/NativeAPM'; import { NativeInstabug } from '../native/NativeInstabug'; @@ -48,29 +47,6 @@ export const setAutoUITraceEnabled = (isEnabled: boolean) => { NativeAPM.setAutoUITraceEnabled(isEnabled); }; -/** - * Starts a custom execution trace. - * - * Returns a promise which resolves with the trace reference if APM is enabled; otherwise, the promise is rejected. - * - * @param name - The name of the trace to start. - * @returns A promise that resolves with a Trace object. - * - * @deprecated Please migrate to the App Flows APIs: {@link startFlow}, {@link endFlow}, and {@link setFlowAttribute}. - */ -export const startExecutionTrace = async (name: string): Promise => { - const TRACE_NOT_STARTED_APM_NOT_ENABLED = `Execution trace "${name}" wasn't created. Please make sure to enable APM first by following the instructions at this link: https://docs.instabug.com/reference#enable-or-disable-apm`; - const timestamp = Date.now() + ''; - - const id = await NativeAPM.startExecutionTrace(name, timestamp); - - if (!id) { - throw new Error(TRACE_NOT_STARTED_APM_NOT_ENABLED); - } - - return new Trace(id, name); -}; - /** * Starts an AppFlow with the specified name. * diff --git a/src/modules/BugReporting.ts b/src/modules/BugReporting.ts index 486169ecc..c514e1ba9 100644 --- a/src/modules/BugReporting.ts +++ b/src/modules/BugReporting.ts @@ -245,7 +245,10 @@ export const setDisclaimerText = (text: string) => { * Sets a minimum number of characters as a requirement for the comments field in the different report types. * @param limit int number of characters. * @param reportTypes (Optional) Array of reportType. If it's not passed, the limit will apply to all report types. + * @platform iOS */ export const setCommentMinimumCharacterCount = (limit: number, reportTypes?: ReportType[]) => { - NativeBugReporting.setCommentMinimumCharacterCount(limit, reportTypes ?? []); + if (Platform.OS === 'ios') { + NativeBugReporting.setCommentMinimumCharacterCount(limit, reportTypes ?? []); + } }; diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index 757ac2576..96177cd92 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -1,10 +1,4 @@ -import { - AppState, - type AppStateStatus, - findNodeHandle, - Platform, - processColor, -} from 'react-native'; +import { AppState, type AppStateStatus, findNodeHandle, Platform } from 'react-native'; import type { NavigationContainerRefWithCurrent, @@ -42,6 +36,7 @@ import { NativeNetworkLogger } from '../native/NativeNetworkLogger'; import InstabugConstants from '../utils/InstabugConstants'; import { InstabugRNConfig } from '../utils/config'; import { Logger } from '../utils/logger'; +import type { ThemeConfig } from '../models/ThemeConfig'; let _currentScreen: string | null = null; let _lastScreen: string | null = null; @@ -401,9 +396,10 @@ export const setColorTheme = (sdkTheme: ColorTheme) => { * To use, import processColor and pass to it with argument the color hex * as argument. * @param color A color to set the UI elements of the SDK to. + * @deprecated Please migrate to the new UI customization API: {@link setTheme} */ export const setPrimaryColor = (color: string) => { - NativeInstabug.setPrimaryColor(processColor(color)); + NativeInstabug.setTheme({ primaryColor: color }); }; /** @@ -787,35 +783,6 @@ export const reportScreenChange = (screenName: string) => { NativeInstabug.reportScreenChange(screenName); }; -/** - * Add experiments to next report. - * @param experiments An array of experiments to add to the next report. - * - * @deprecated Please migrate to the new Feature Flags APIs: {@link addFeatureFlags}. - */ -export const addExperiments = (experiments: string[]) => { - NativeInstabug.addExperiments(experiments); -}; - -/** - * Remove experiments from next report. - * @param experiments An array of experiments to remove from the next report. - * - * @deprecated Please migrate to the new Feature Flags APIs: {@link removeFeatureFlags}. - */ -export const removeExperiments = (experiments: string[]) => { - NativeInstabug.removeExperiments(experiments); -}; - -/** - * Clear all experiments - * - * @deprecated Please migrate to the new Feature Flags APIs: {@link removeAllFeatureFlags}. - */ -export const clearAllExperiments = () => { - NativeInstabug.clearAllExperiments(); -}; - /** * Add feature flags to the next report. * @param featureFlags An array of feature flags to add to the next report. @@ -907,3 +874,50 @@ export const _registerFeatureFlagsChangeListener = ( export const enableAutoMasking = (autoMaskingTypes: AutoMaskingType[]) => { NativeInstabug.enableAutoMasking(autoMaskingTypes); }; + +/** + * Sets a custom theme for Instabug UI elements. + * + * This method provides comprehensive theming support. It will automatically use IBGTheme + * if available in the SDK version, otherwise falls back to individual theming methods. + * + * @param theme - Configuration object containing theme properties + * + * @example + * ```typescript + * // Basic usage with primary color (always supported) + * Instabug.setTheme({ + * primaryColor: '#FF6B6B' + * }); + * + * // Comprehensive theming (uses IBGTheme when available) + * Instabug.setTheme({ + * primaryColor: '#FF6B6B', + * secondaryTextColor: '#666666', + * primaryTextColor: '#333333', + * titleTextColor: '#000000', + * backgroundColor: '#FFFFFF', + * primaryTextStyle: 'bold', + * secondaryTextStyle: 'normal', + * titleTextStyle: 'bold', + * ctaTextStyle: 'bold', + * primaryFontPath: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + * secondaryFontPath: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + * ctaTextType: '/data/user/0/com.yourapp/files/fonts/YourFont.ttf', + * primaryFontAsset: 'fonts/YourFont.ttf', + * secondaryFontAsset: 'fonts/YourFont.ttf' + * }); + * ``` + */ +export const setTheme = (theme: ThemeConfig) => { + NativeInstabug.setTheme(theme); +}; +/** + * Enables or disables displaying in full-screen mode, hiding the status and navigation bars. + * @param isEnabled A boolean to enable/disable setFullscreen. + */ +export const setFullscreen = (isEnabled: boolean) => { + if (Platform.OS === 'android') { + NativeInstabug.setFullscreen(isEnabled); + } +}; diff --git a/src/native/NativeAPM.ts b/src/native/NativeAPM.ts index 9fa30b702..86d017167 100644 --- a/src/native/NativeAPM.ts +++ b/src/native/NativeAPM.ts @@ -34,10 +34,6 @@ export interface ApmNativeModule extends NativeModule { endAppLaunch(): void; // Execution Traces APIs // - startExecutionTrace(name: string, timestamp: string): Promise; - setExecutionTraceAttribute(id: string, key: string, value: string): void; - endExecutionTrace(id: string): void; - // App Flows APIs // startFlow(name: string): void; endFlow(name: string): void; diff --git a/src/native/NativeInstabug.ts b/src/native/NativeInstabug.ts index f798bf4d2..c350d705f 100644 --- a/src/native/NativeInstabug.ts +++ b/src/native/NativeInstabug.ts @@ -14,6 +14,7 @@ import type { import type { NativeConstants } from './NativeConstants'; import type { W3cExternalTraceAttributes } from '../models/W3cExternalTraceAttributes'; import { NativeModules } from './NativePackage'; +import type { ThemeConfig } from '../models/ThemeConfig'; export interface InstabugNativeModule extends NativeModule { getConstants(): NativeConstants; @@ -123,10 +124,6 @@ export interface InstabugNativeModule extends NativeModule { getTags(): Promise; // Experiments APIs // - addExperiments(experiments: string[]): void; - removeExperiments(experiments: string[]): void; - clearAllExperiments(): void; - addFeatureFlags(featureFlags: Record): void; removeFeatureFlags(featureFlags: string[]): void; @@ -163,9 +160,12 @@ export interface InstabugNativeModule extends NativeModule { setOnFeaturesUpdatedListener(handler?: (params: any) => void): void; // android only enableAutoMasking(autoMaskingTypes: AutoMaskingType[]): void; getNetworkBodyMaxSize(): Promise; + + setTheme(theme: ThemeConfig): void; + setFullscreen(isEnabled: boolean): void; } -export const NativeInstabug = NativeModules.Instabug; +export const NativeInstabug = NativeModules.Instabug as InstabugNativeModule; export enum NativeEvents { PRESENDING_HANDLER = 'IBGpreSendingHandler', diff --git a/test/mocks/mockAPM.ts b/test/mocks/mockAPM.ts index 27644c694..7a9c5bac9 100644 --- a/test/mocks/mockAPM.ts +++ b/test/mocks/mockAPM.ts @@ -6,9 +6,6 @@ const mockAPM: ApmNativeModule = { setEnabled: jest.fn(), setAppLaunchEnabled: jest.fn(), setAutoUITraceEnabled: jest.fn(), - startExecutionTrace: jest.fn(), - setExecutionTraceAttribute: jest.fn(), - endExecutionTrace: jest.fn(), startFlow: jest.fn(), setFlowAttribute: jest.fn(), endFlow: jest.fn(), diff --git a/test/mocks/mockInstabug.ts b/test/mocks/mockInstabug.ts index d3d5206b5..e6f41fc73 100644 --- a/test/mocks/mockInstabug.ts +++ b/test/mocks/mockInstabug.ts @@ -50,9 +50,6 @@ const mockInstabug: InstabugNativeModule = { setPreSendingHandler: jest.fn(), reportScreenChange: jest.fn(), reportCurrentViewChange: jest.fn(), - addExperiments: jest.fn(), - removeExperiments: jest.fn(), - clearAllExperiments: jest.fn(), networkLogIOS: jest.fn(), networkLogAndroid: jest.fn(), addFeatureFlags: jest.fn(), @@ -78,6 +75,8 @@ const mockInstabug: InstabugNativeModule = { setOnFeaturesUpdatedListener: jest.fn(), enableAutoMasking: jest.fn(), getNetworkBodyMaxSize: jest.fn().mockResolvedValue(10240), // 10 KB + setTheme: jest.fn(), + setFullscreen: jest.fn(), }; export default mockInstabug; diff --git a/test/models/Trace.spec.ts b/test/models/Trace.spec.ts deleted file mode 100644 index 8421b419a..000000000 --- a/test/models/Trace.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import Trace from '../../src/models/Trace'; -import { NativeAPM } from '../../src/native/NativeAPM'; - -describe('Trace Model', () => { - it('should set the id, name and attributes if passed', () => { - const id = 'trace-id'; - const name = 'my-trace'; - const attributes = { screen: 'login' }; - const trace = new Trace(id, name, attributes); - - expect(trace.id).toBe(id); - expect(trace.name).toBe(name); - expect(trace.attributes).toBe(attributes); - }); - - it('should set execution trace attributes', () => { - const attribute = { key: 'isAuthenticated', value: 'yes' }; - - const trace = new Trace('trace-id'); - trace.setAttribute(attribute.key, attribute.value); - - expect(trace.attributes[attribute.key]).toBe(attribute.value); - expect(NativeAPM.setExecutionTraceAttribute).toBeCalledTimes(1); - expect(NativeAPM.setExecutionTraceAttribute).toBeCalledWith( - trace.id, - attribute.key, - attribute.value, - ); - }); - - it('should end execution trace', () => { - const trace = new Trace('trace-id'); - - trace.end(); - - expect(NativeAPM.endExecutionTrace).toBeCalledTimes(1); - expect(NativeAPM.endExecutionTrace).toBeCalledWith(trace.id); - }); -}); diff --git a/test/modules/APM.spec.ts b/test/modules/APM.spec.ts index cf932d25c..ea703d3ea 100644 --- a/test/modules/APM.spec.ts +++ b/test/modules/APM.spec.ts @@ -1,11 +1,8 @@ import { Platform } from 'react-native'; -import { mocked } from 'jest-mock'; - -import Trace from '../../src/models/Trace'; -import * as APM from '../../src/modules/APM'; import { NativeAPM } from '../../src/native/NativeAPM'; import { NativeInstabug } from '../../src/native/NativeInstabug'; +import * as APM from '../../src/modules/APM'; describe('APM Module', () => { it('should call the native method setEnabled', () => { @@ -51,57 +48,6 @@ describe('APM Module', () => { expect(NativeAPM.setAutoUITraceEnabled).toBeCalledWith(true); }); - it('should call the native method startExecutionTrace', () => { - mocked(NativeAPM).startExecutionTrace.mockResolvedValueOnce('trace-id'); - - APM.startExecutionTrace('trace'); - - expect(NativeAPM.startExecutionTrace).toBeCalledTimes(1); - expect(NativeAPM.startExecutionTrace).toBeCalledWith('trace', expect.any(String)); - }); - - it("should throw an error if native startExecutionTrace didn't return an ID", async () => { - mocked(NativeAPM).startExecutionTrace.mockResolvedValueOnce(null); - const promise = APM.startExecutionTrace('trace'); - - await expect(promise).rejects.toThrowError(/trace "trace" wasn't created/i); - }); - - it('should resolve with an Trace object if native startExecutionTrace returned an ID', async () => { - mocked(NativeAPM).startExecutionTrace.mockResolvedValueOnce('trace-id'); - - const promise = APM.startExecutionTrace('trace'); - - await expect(promise).resolves.toBeInstanceOf(Trace); - await expect(promise).resolves.toHaveProperty('name', 'trace'); - }); - - it('should call the native method setExecutionTraceAttribute', () => { - mocked(NativeAPM).startExecutionTrace.mockResolvedValueOnce('trace-id'); - - APM.startExecutionTrace('trace').then((trace) => { - trace.setAttribute('key', 'value'); - - expect(NativeAPM.setExecutionTraceAttribute).toBeCalledTimes(1); - expect(NativeAPM.setExecutionTraceAttribute).toBeCalledWith( - expect.any(String), - 'key', - 'value', - ); - }); - }); - - it('should call the native method endExecutionTrace', () => { - mocked(NativeAPM).startExecutionTrace.mockResolvedValueOnce('trace-id'); - - APM.startExecutionTrace('trace').then((trace) => { - trace.end(); - - expect(NativeAPM.endExecutionTrace).toBeCalledTimes(1); - expect(NativeAPM.endExecutionTrace).toBeCalledWith(expect.any(String)); - }); - }); - it('should call the native method startFlow', () => { const appFlowName = 'flowName'; diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index 7ec887f83..bd21b3efc 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -1,7 +1,7 @@ import '../mocks/mockInstabugUtils'; import '../mocks/mockNetworkLogger'; -import { findNodeHandle, Platform, processColor } from 'react-native'; +import { findNodeHandle, Platform } from 'react-native'; import type { NavigationContainerRefWithCurrent } from '@react-navigation/native'; // Import the hook import { mocked } from 'jest-mock'; import waitForExpect from 'wait-for-expect'; @@ -464,12 +464,13 @@ describe('Instabug Module', () => { expect(NativeInstabug.setColorTheme).toBeCalledWith(theme); }); - it('should call the native method setPrimaryColor', () => { + it('should call the native method setPrimaryColor on iOS', () => { + Platform.OS = 'ios'; const color = '#fff'; - Instabug.setPrimaryColor(color); + Instabug.setTheme({ primaryColor: color }); - expect(NativeInstabug.setPrimaryColor).toBeCalledTimes(1); - expect(NativeInstabug.setPrimaryColor).toBeCalledWith(processColor(color)); + expect(NativeInstabug.setTheme).toBeCalledTimes(1); + expect(NativeInstabug.setTheme).toBeCalledWith({ primaryColor: color }); }); it('should call the native method appendTags', () => { @@ -834,25 +835,6 @@ describe('Instabug Module', () => { expect(emitter.listenerCount(NativeEvents.PRESENDING_HANDLER)).toBe(1); }); - it('should call native addExperiments method', () => { - const experiments = ['exp1', 'exp2']; - Instabug.addExperiments(experiments); - expect(NativeInstabug.addExperiments).toBeCalledTimes(1); - expect(NativeInstabug.addExperiments).toBeCalledWith(experiments); - }); - - it('should call native removeExperiments method', () => { - const experiments = ['exp1', 'exp2']; - Instabug.removeExperiments(experiments); - expect(NativeInstabug.removeExperiments).toBeCalledTimes(1); - expect(NativeInstabug.removeExperiments).toBeCalledWith(experiments); - }); - - it('should call native clearAllExperiments method', () => { - Instabug.clearAllExperiments(); - expect(NativeInstabug.clearAllExperiments).toBeCalledTimes(1); - }); - it('should call native addFeatureFlags method', () => { const featureFlags: Array = [ { From b6089f806c9a8e2fa8b5c81467a3ec7766fd55f5 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:13:37 +0300 Subject: [PATCH 09/20] bump ios SDK to 16.0.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57b6bb5ac..5af4b937c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ - **BREAKING** Remove deprecated APIs ([#1424](https://github.com/Instabug/Instabug-React-Native/pull/1424)). See migration guide for more details. +- Bump Instabug iOS SDK to v16.0.1 ([#1436](https://github.com/Instabug/Instabug-React-Native/pull/1436)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/16.0.1). + +- Bump Instabug Android SDK to v16.0.0 ([#1436](https://github.com/Instabug/Instabug-React-Native/pull/1436)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v16.0.1). + ### Fixed - Masking private views on newer React native Versions ([#1403](https://github.com/Instabug/Instabug-React-Native/pull/1403)) From fb1bd69b9055500269a25b3f21e57cfe3c58031b Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:19:59 +0300 Subject: [PATCH 10/20] bump ios SDK to 16.0.1 --- examples/default/ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index d9f1e9e13..4ab5f15fc 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -2098,6 +2098,6 @@ SPEC CHECKSUMS: SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 055f92ad73f8c8600a93f0e25ac0b2344c3b07e6 -PODFILE CHECKSUM: 837b933596e1616ff02cc206bb17dee4f611fdbc +PODFILE CHECKSUM: 6cf5149b0b50ad7d6806d761fc2014ce27bae779 COCOAPODS: 1.14.0 From b28e763529e978b26e7094e0a403b4ca11838e99 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:39:29 +0300 Subject: [PATCH 11/20] feat: remove deprecated APis --- examples/default/ios/Podfile.lock | 4 ++-- ios/RNInstabug/InstabugAPMBridge.m | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 4ab5f15fc..01837ec1a 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -2090,7 +2090,7 @@ SPEC CHECKSUMS: ReactCommon: 6a952e50c2a4b694731d7682aaa6c79bc156e4ad RNCClipboard: 2821ac938ef46f736a8de0c8814845dde2dcbdfb RNGestureHandler: 511250b190a284388f9dd0d2e56c1df76f14cfb8 - RNInstabug: abe8381b594fea43eddceb6f01a53a6bf380a967 + RNInstabug: 6cf5149b0b50ad7d6806d761fc2014ce27bae779 RNReanimated: f42a5044d121d68e91680caacb0293f4274228eb RNScreens: c7ceced6a8384cb9be5e7a5e88e9e714401fd958 RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d @@ -2098,6 +2098,6 @@ SPEC CHECKSUMS: SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 055f92ad73f8c8600a93f0e25ac0b2344c3b07e6 -PODFILE CHECKSUM: 6cf5149b0b50ad7d6806d761fc2014ce27bae779 +PODFILE CHECKSUM: 837b933596e1616ff02cc206bb17dee4f611fdbc COCOAPODS: 1.14.0 diff --git a/ios/RNInstabug/InstabugAPMBridge.m b/ios/RNInstabug/InstabugAPMBridge.m index 29319264d..61c48af93 100644 --- a/ios/RNInstabug/InstabugAPMBridge.m +++ b/ios/RNInstabug/InstabugAPMBridge.m @@ -2,7 +2,6 @@ #import "InstabugAPMBridge.h" #import -#import #import #import #import @@ -27,8 +26,6 @@ + (BOOL)requiresMainQueueSetup RCT_EXPORT_MODULE(IBGAPM) -NSMutableDictionary *traces; - - (id) init { self = [super init]; From 278737a9dcbef5d047fb2e01508b015a66c26097 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:22:33 +0300 Subject: [PATCH 12/20] feat: remove deprecated APis --- ios/RNInstabug/InstabugAPMBridge.m | 1 - 1 file changed, 1 deletion(-) diff --git a/ios/RNInstabug/InstabugAPMBridge.m b/ios/RNInstabug/InstabugAPMBridge.m index 61c48af93..ab49af6fb 100644 --- a/ios/RNInstabug/InstabugAPMBridge.m +++ b/ios/RNInstabug/InstabugAPMBridge.m @@ -29,7 +29,6 @@ + (BOOL)requiresMainQueueSetup - (id) init { self = [super init]; - traces = [[NSMutableDictionary alloc] init]; return self; } From 46c64df45446cd0d0092780a028aee0f2cc45205 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:00:36 +0300 Subject: [PATCH 13/20] feat: remove deprecated APis --- examples/default/ios/InstabugTests/InstabugSampleTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 70ea9f2de..50e1cb2b9 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -196,7 +196,7 @@ - (void)testIdentifyUser { NSString *email = @"em@il.com"; NSString *name = @"this is my name"; - OCMStub([mock identifyUserWithEmail:email name:name]); + OCMStub([mock identifyUserWithID:nil email:email name:name]); [self.instabugBridge identifyUser:email name:name userId:nil]; OCMVerify([mock identifyUserWithID:nil email:email name:name]); } @@ -629,7 +629,7 @@ - (void)testGetNetworkBodyMaxSize { - (void)testSetTheme { id mock = OCMClassMock([Instabug class]); id mockTheme = OCMClassMock([IBGTheme class]); - + // Create theme configuration dictionary NSDictionary *themeConfig = @{ @"primaryColor": @"#FF0000", From 69d66c5d8f54695399ca60ed6ccaf88d148940af Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 18 Aug 2025 15:41:43 +0300 Subject: [PATCH 14/20] feat: support expo updates (#1391) * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * feat: xcode 16 support * chore: integrate iOS expo updates support custom build * feat[ios]: add setOverAirVersion * feat[ios]: test setOverAirVersion * feat: add setOverAirVersion * feat: test setOverAirVersion * chore[ios]: revert old cocoapods version * chore(example): revert old example app * chore (example): fix lint * chore(android): integrate with expo updates android snapshot * feat(android): add setOverAirVersion API * chore: add changelog item * chore(android): remove log * chore(iOS): fix failing CI * chore(ios): workaround failing CI * ci: fix ci running * ci: fix ci running * chore(android): add dependency * chore(CI): fix sync_generated_files task * chore(CI): fix sync_generated_files task * chore(android): update snapshot * chore(CI): fix ios sync * chore: fix CI sync_generated_files job * hore: fix CI sync_generated_files job * chore(ios): update custom build * chore: fix CI sync_generated_files job * fix(ios): service type mismatch * fix(ios): fix test SetOverAirVersion * fix(ios): fix test SetOverAirVersion * chore: fix CI job * chore: revert old podfile.lock * feat: add new ios custom build * chore: add latest android snapshot * fix: type conversion * fix: ios test cases * fix: migration issue * fix: migration issue * fix: podfile issue --------- Co-authored-by: Ahmed alaa Co-authored-by: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> --- CHANGELOG.md | 2 + .../instabug/reactlibrary/ArgsRegistry.java | 7 ++++ .../com/instabug/reactlibrary/RNInstabug.java | 27 ++++++++++++ .../RNInstabugReactnativeModule.java | 31 +++++++++++++- .../RNInstabugReactnativeModuleTest.java | 16 ++++++++ .../ios/InstabugTests/InstabugSampleTests.m | 27 +++++++++++- .../ios/InstabugTests/RNInstabugTests.m | 22 ++++++++++ examples/default/ios/Podfile | 2 +- examples/default/ios/Podfile.lock | 2 +- examples/default/src/App.tsx | 2 + examples/default/src/screens/HomeScreen.tsx | 1 - ios/RNInstabug/ArgsRegistry.h | 1 + ios/RNInstabug/ArgsRegistry.m | 8 ++++ ios/RNInstabug/InstabugReactBridge.h | 4 +- ios/RNInstabug/InstabugReactBridge.m | 8 +++- ios/RNInstabug/RNInstabug.h | 3 ++ ios/RNInstabug/RNInstabug.m | 5 +++ src/models/InstabugConfig.ts | 6 +++ src/models/OverAirUpdate.ts | 14 +++++++ src/modules/Instabug.ts | 13 ++++++ src/native/NativeConstants.ts | 5 +++ src/native/NativeInstabug.ts | 3 ++ src/utils/Enums.ts | 8 ++++ test/mocks/mockInstabug.ts | 1 + test/modules/Instabug.spec.ts | 41 ++++++++++++++++++- 25 files changed, 249 insertions(+), 10 deletions(-) create mode 100644 src/models/OverAirUpdate.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5af4b937c..920652cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ ### Added +- Add support for expo updates versioning ([#1391](https://github.com/Instabug/Instabug-React-Native/pull/1391)) + - Add support enable/disable screenshot auto masking. ([#1389](https://github.com/Instabug/Instabug-React-Native/pull/1389)) - Add support for BugReporting user consents. ([#1383](https://github.com/Instabug/Instabug-React-Native/pull/1383)) diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index 15fa45a1d..3c0175198 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -17,6 +17,7 @@ import com.instabug.library.invocation.util.InstabugVideoRecordingButtonPosition; import com.instabug.library.sessionreplay.model.SessionMetadata; import com.instabug.library.ui.onboarding.WelcomeMessage; +import com.instabug.library.util.overairversion.OverAirVersionType; import com.instabug.library.MaskingType; import java.util.ArrayList; @@ -61,6 +62,7 @@ static Map getAll() { putAll(locales); putAll(placeholders); putAll(launchType); + putAll(overAirUpdateService); putAll(autoMaskingTypes); putAll(userConsentActionType); }}; @@ -255,6 +257,11 @@ static Map getAll() { put("warm",SessionMetadata.LaunchType.WARM ); put("unknown","unknown"); }}; + + public static ArgsMap overAirUpdateService = new ArgsMap() {{ + put("expo", OverAirVersionType.EXPO); + put("codePush",OverAirVersionType.CODE_PUSH ); + }}; // Temporary workaround to be removed in future release // This is used for mapping native `LaunchType` values into React Native enum values. diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java index 28de7d950..716fe0e6a 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabug.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.facebook.react.bridge.ReadableMap; import com.instabug.apm.APM; import com.instabug.library.Instabug; import com.instabug.library.LogLevel; @@ -175,6 +176,11 @@ public static class Builder { */ private String codePushVersion; + /** + * The overAirUpdate Version to be used for all reports. + */ + private ReadableMap overAirVersion; + /** * The events that trigger the SDK's user interface. */ @@ -235,6 +241,16 @@ public Builder setCodePushVersion(String codePushVersion) { return this; } + /** + * Sets over air update version to be used for all reports. + * + * @param overAirVersion the over air update version and service map. + */ + public Builder setOverAirVersion(ReadableMap overAirVersion) { + this.overAirVersion = overAirVersion; + return this; + } + /** * Sets flag to override SDK screenshot security behavior. * @@ -289,6 +305,17 @@ public void build() { instabugBuilder.ignoreFlagSecure(ignoreFlagSecure); } + if (overAirVersion != null ) { + if (overAirVersion.hasKey("service") && overAirVersion.hasKey("version")) + { + if (overAirVersion.getString("service")!=null && overAirVersion.getString("version")!=null) + { + instabugBuilder.setOverAirVersion(overAirVersion.getString("version"), + ArgsRegistry.overAirUpdateService.get(overAirVersion.getString("service"))); + } + } + } + instabugBuilder.build(); // Temporarily disabling APM hot launches diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 0dffd60c2..68a775fe3 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -157,7 +157,10 @@ public void init( final boolean useNativeNetworkInterception, @Nullable final String codePushVersion, @Nullable final String appVariant, - final ReadableMap map + final ReadableMap map, + @Nullable final ReadableMap overAirVersion + + ) { MainThreadHandler.runOnMainThread(new Runnable() { @Override @@ -189,6 +192,16 @@ public void run() { if (appVariant != null) { builder.setAppVariant(appVariant); } + + if(overAirVersion != null ) { + if(Instabug.isBuilt()) { + Instabug.setOverAirVersion(overAirVersion.getString("version"), + ArgsRegistry.overAirUpdateService.get(overAirVersion.getString("service"))); + } else { + builder.setOverAirVersion(overAirVersion); + } + } + builder.build(); } }); @@ -208,6 +221,22 @@ public void run() { }); } + @ReactMethod + public void setOverAirVersion(@Nullable final ReadableMap overAirVersion) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Instabug.setOverAirVersion(overAirVersion.getString("version"), + ArgsRegistry.overAirUpdateService.get(overAirVersion.getString("service"))); + + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + /** * Adds tag(s) to issues before sending them diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index 3a0edc5f0..3751bea71 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -24,6 +24,7 @@ import com.instabug.library.featuresflags.model.IBGFeatureFlag; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.ui.onboarding.WelcomeMessage; +import com.instabug.library.util.overairversion.OverAirVersionType; import com.instabug.reactlibrary.utils.MainThreadHandler; import com.instabug.library.MaskingType; @@ -202,6 +203,21 @@ public void testSetCodePushVersion() { mockInstabug.verify(() -> Instabug.setCodePushVersion(codePushVersion)); } + @Test + public void testSetOverAirVersion() { + WritableMap mockMap = mock(WritableMap.class); + + String version="D0A12345-6789-4B3C-A123-4567ABCDEF0"; + + when(mockMap.getString("version")).thenReturn(version); + when(mockMap.getString("service")).thenReturn("expo"); + + rnModule.setOverAirVersion(mockMap); + + mockInstabug.verify(() -> Instabug.setOverAirVersion( + version, OverAirVersionType.EXPO)); + } + @Test public void testIdentifyUserWithNoId() { // given diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 50e1cb2b9..9798ed665 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -72,20 +72,29 @@ - (void)testInit { NSString *appVariant = @"variant 1"; NSArray *invocationEvents = [NSArray arrayWithObjects:[NSNumber numberWithInteger:floatingButtonInvocationEvent], nil]; + NSDictionary *overAirVersion = @{ + @"service":@"expo", + @"version":@"D0A12345-6789-4B3C-A123-4567ABCDEF01" + }; BOOL useNativeNetworkInterception = YES; IBGSDKDebugLogsLevel sdkDebugLogsLevel = IBGSDKDebugLogsLevelDebug; + IBGOverAirType service = [ArgsRegistry.overAirServices[overAirVersion[@"service"]] intValue]; OCMStub([mock setCodePushVersion:codePushVersion]); + OCMStub([mock setOverAirVersion:overAirVersion[@"version"] withType:service]); - [self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception codePushVersion:codePushVersion appVariant:appVariant options:nil ]; + [self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception codePushVersion:codePushVersion appVariant:appVariant options:nil overAirVersion:overAirVersion ]; OCMVerify([mock setCodePushVersion:codePushVersion]); + OCMVerify([mock setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]); + + XCTAssertEqual(Instabug.appVariant, appVariant); OCMVerify([self.mRNInstabug initWithToken:appToken invocationEvents:floatingButtonInvocationEvent debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception]); } -- (void)testSetCodePushVersion { +- (void)test { id mock = OCMClassMock([Instabug class]); NSString *codePushVersion = @"123"; @@ -94,6 +103,20 @@ - (void)testSetCodePushVersion { OCMVerify([mock setCodePushVersion:codePushVersion]); } +- (void)testSetOverAirVersion { + id mock = OCMClassMock([Instabug class]); + NSDictionary *overAirVersion = @{ + @"service":@"expo", + @"version":@"D0A12345-6789-4B3C-A123-4567ABCDEF01" + }; + + [self.instabugBridge setOverAirVersion:overAirVersion]; + + IBGOverAirType service = [ArgsRegistry.overAirServices[overAirVersion[@"service"]] intValue]; + + OCMVerify([mock setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]); +} + - (void)testSetUserData { id mock = OCMClassMock([Instabug class]); NSString *userData = @"user_data"; diff --git a/examples/default/ios/InstabugTests/RNInstabugTests.m b/examples/default/ios/InstabugTests/RNInstabugTests.m index abf355614..69a1b0fcd 100644 --- a/examples/default/ios/InstabugTests/RNInstabugTests.m +++ b/examples/default/ios/InstabugTests/RNInstabugTests.m @@ -73,4 +73,26 @@ - (void) testSetCodePushVersion { OCMVerify([self.mInstabug setCodePushVersion:codePushVersion]); } +- (void)testSetOverAirVersionExpo { + NSDictionary *overAirVersion = @{ + @"service":@(IBGOverAirTypeExpo), + @"version":@"D0A12345-6789-4B3C-A123-4567ABCDEF01" + }; + + [RNInstabug setOverAirVersion:overAirVersion]; + + OCMVerify([self.mInstabug setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]); +} + +- (void)testSetOverAirVersionCodepush { + NSDictionary *overAirVersion = @{ + @"service":@(IBGOverAirTypeCodePush), + @"version":@"2.0.0" + }; + + [RNInstabug setOverAirVersion:overAirVersion]; + + OCMVerify([self.mInstabug setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]); +} + @end diff --git a/examples/default/ios/Podfile b/examples/default/ios/Podfile index e1dda08e3..f5b776d5b 100644 --- a/examples/default/ios/Podfile +++ b/examples/default/ios/Podfile @@ -16,7 +16,7 @@ target 'InstabugExample' do rn_maps_path = '../node_modules/react-native-maps' pod 'react-native-google-maps', :path => rn_maps_path - # Flags change depending on the env values. + # add this line flags = get_default_flags() use_react_native!( diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 01837ec1a..0e43c84ef 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -2098,6 +2098,6 @@ SPEC CHECKSUMS: SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 055f92ad73f8c8600a93f0e25ac0b2344c3b07e6 -PODFILE CHECKSUM: 837b933596e1616ff02cc206bb17dee4f611fdbc +PODFILE CHECKSUM: ac02ce223b69d8b05ac5901eaaac229f19e6d881 COCOAPODS: 1.14.0 diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index 82be996c5..540086a97 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -13,6 +13,7 @@ import Instabug, { NetworkLogger, ReproStepsMode, SessionReplay, + OverAirUpdateServices, } from 'instabug-reactnative'; import { NativeBaseProvider } from 'native-base'; @@ -50,6 +51,7 @@ export const App: React.FC = () => { debugLogsLevel: LogLevel.verbose, networkInterceptionMode: NetworkInterceptionMode.javascript, appVariant: 'App variant', + overAirVersion: { service: OverAirUpdateServices.codePush, version: '1.0.0' }, }); CrashReporting.setNDKCrashesEnabled(true); diff --git a/examples/default/src/screens/HomeScreen.tsx b/examples/default/src/screens/HomeScreen.tsx index 7a5aeeb46..bee9905e6 100644 --- a/examples/default/src/screens/HomeScreen.tsx +++ b/examples/default/src/screens/HomeScreen.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; - import { ListTile } from '../components/ListTile'; import { Screen } from '../components/Screen'; import type { HomeStackParamList } from '../navigation/HomeStack'; diff --git a/ios/RNInstabug/ArgsRegistry.h b/ios/RNInstabug/ArgsRegistry.h index c760ae36c..2197a8c31 100644 --- a/ios/RNInstabug/ArgsRegistry.h +++ b/ios/RNInstabug/ArgsRegistry.h @@ -23,6 +23,7 @@ typedef NSDictionary ArgsDictionary; + (ArgsDictionary *) locales; + (ArgsDictionary *)nonFatalExceptionLevel; + (ArgsDictionary *) launchType; ++ (ArgsDictionary *) overAirServices; + (ArgsDictionary *) userConsentActionTypes; + (NSDictionary *) placeholders; diff --git a/ios/RNInstabug/ArgsRegistry.m b/ios/RNInstabug/ArgsRegistry.m index 8fb1e9b77..b96229775 100644 --- a/ios/RNInstabug/ArgsRegistry.m +++ b/ios/RNInstabug/ArgsRegistry.m @@ -21,6 +21,8 @@ + (NSMutableDictionary *) getAll { [all addEntriesFromDictionary:ArgsRegistry.nonFatalExceptionLevel]; [all addEntriesFromDictionary:ArgsRegistry.placeholders]; [all addEntriesFromDictionary:ArgsRegistry.launchType]; + [all addEntriesFromDictionary:ArgsRegistry.overAirServices]; + [all addEntriesFromDictionary:ArgsRegistry.autoMaskingTypes]; [all addEntriesFromDictionary:ArgsRegistry.userConsentActionTypes]; @@ -256,6 +258,12 @@ + (ArgsDictionary *) launchType { @"unknown":@(LaunchTypeUnknown) }; } ++ (ArgsDictionary *) overAirServices { + return @{ + @"expo":@(IBGOverAirTypeExpo) , + @"codePush":@(IBGOverAirTypeCodePush), + }; +} + (ArgsDictionary *)autoMaskingTypes { return @{ diff --git a/ios/RNInstabug/InstabugReactBridge.h b/ios/RNInstabug/InstabugReactBridge.h index 187caa6c6..79f8c1c23 100644 --- a/ios/RNInstabug/InstabugReactBridge.h +++ b/ios/RNInstabug/InstabugReactBridge.h @@ -26,10 +26,12 @@ - (void)setEnabled:(BOOL)isEnabled; -- (void)init:(NSString *)token invocationEvents:(NSArray *)invocationEventsArray debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel useNativeNetworkInterception:(BOOL)useNativeNetworkInterception codePushVersion:(NSString *)codePushVersion appVariant:(NSString *)appVariant options:(nullable NSDictionary *)options; +- (void)init:(NSString *)token invocationEvents:(NSArray *)invocationEventsArray debugLogsLevel:(IBGSDKDebugLogsLevel)sdkDebugLogsLevel useNativeNetworkInterception:(BOOL)useNativeNetworkInterception codePushVersion:(NSString *)codePushVersion appVariant:(NSString *)appVariant options:(nullable NSDictionary *)options overAirVersion:(NSDictionary *)overAirVersion; - (void)setCodePushVersion:(NSString *)version; +- (void)setOverAirVersion:(NSDictionary *)overAirVersion; + - (void)setUserData:(NSString *)userData; - (void)setAppVariant:(NSString *)appVariant; diff --git a/ios/RNInstabug/InstabugReactBridge.m b/ios/RNInstabug/InstabugReactBridge.m index fe9e11e8a..1fb21af3f 100644 --- a/ios/RNInstabug/InstabugReactBridge.m +++ b/ios/RNInstabug/InstabugReactBridge.m @@ -44,7 +44,7 @@ - (dispatch_queue_t)methodQueue { codePushVersion:(NSString *)codePushVersion appVariant:(NSString *)appVariant options:(nullable NSDictionary *)options - + overAirVersion :(NSDictionary *)overAirVersion ) { if(appVariant != nil){ @@ -59,6 +59,8 @@ - (dispatch_queue_t)methodQueue { [Instabug setCodePushVersion:codePushVersion]; + [Instabug setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]; + [RNInstabug initWithToken:token invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel @@ -69,6 +71,10 @@ - (dispatch_queue_t)methodQueue { [Instabug setCodePushVersion:version]; } +RCT_EXPORT_METHOD(setOverAirVersion:(NSDictionary *)overAirVersion) { + [Instabug setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]; +} + RCT_EXPORT_METHOD(setAppVariant:(NSString *)appVariant) { Instabug.appVariant = appVariant; } diff --git a/ios/RNInstabug/RNInstabug.h b/ios/RNInstabug/RNInstabug.h index 70612fef7..1f93a44e4 100644 --- a/ios/RNInstabug/RNInstabug.h +++ b/ios/RNInstabug/RNInstabug.h @@ -2,6 +2,7 @@ #define RNInstabug_h #import +#import "ArgsRegistry.h" @interface RNInstabug : NSObject @@ -27,6 +28,8 @@ useNativeNetworkInterception:(BOOL)useNativeNetworkInterception; */ + (void)setCodePushVersion:(NSString *)codePushVersion; ++ (void)setOverAirVersion:(NSDictionary *)overAirVersion; + @end #endif /* RNInstabug_h */ diff --git a/ios/RNInstabug/RNInstabug.m b/ios/RNInstabug/RNInstabug.m index 3ea51ae59..ccf496ca5 100644 --- a/ios/RNInstabug/RNInstabug.m +++ b/ios/RNInstabug/RNInstabug.m @@ -61,6 +61,11 @@ + (void)setCodePushVersion:(NSString *)codePushVersion { [Instabug setCodePushVersion:codePushVersion]; } ++ (void)setOverAirVersion:(NSDictionary *)overAirVersion { + [Instabug setOverAirVersion:overAirVersion[@"version"] withType:[overAirVersion[@"service"] intValue]]; +} + + // Note: This function is used to bridge IBGNSLog with RCTLogFunction. // This log function should not be used externally and is only an implementation detail. void RNIBGLog(IBGLogLevel logLevel, NSString *format, ...) { diff --git a/src/models/InstabugConfig.ts b/src/models/InstabugConfig.ts index bc3d5c747..4d7e231e0 100644 --- a/src/models/InstabugConfig.ts +++ b/src/models/InstabugConfig.ts @@ -1,4 +1,5 @@ import type { InvocationEvent, LogLevel, NetworkInterceptionMode } from '../utils/Enums'; +import type { OverAirUpdate } from './OverAirUpdate'; export interface InstabugConfig { /** @@ -39,4 +40,9 @@ export interface InstabugConfig { * @default NetworkInterceptionMode.javascript */ networkInterceptionMode?: NetworkInterceptionMode; + + /** + * An optional over air service update version to be used for all reports. + */ + overAirVersion?: OverAirUpdate; } diff --git a/src/models/OverAirUpdate.ts b/src/models/OverAirUpdate.ts new file mode 100644 index 000000000..dd7de040f --- /dev/null +++ b/src/models/OverAirUpdate.ts @@ -0,0 +1,14 @@ +import { OverAirUpdateServices } from '../utils/Enums'; +export interface OverAirUpdate { + /** + * the name of OTA service + * e.g. `codePush` or `expo` + */ + service: OverAirUpdateServices; + + /** + * The version or UUID of the OTA service + */ + + version: string; +} diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index 96177cd92..8bf40e711 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -36,6 +36,7 @@ import { NativeNetworkLogger } from '../native/NativeNetworkLogger'; import InstabugConstants from '../utils/InstabugConstants'; import { InstabugRNConfig } from '../utils/config'; import { Logger } from '../utils/logger'; +import type { OverAirUpdate } from '../models/OverAirUpdate'; import type { ThemeConfig } from '../models/ThemeConfig'; let _currentScreen: string | null = null; @@ -282,6 +283,7 @@ const initializeNativeInstabug = (config: InstabugConfig) => { ignoreAndroidSecureFlag: config.ignoreAndroidSecureFlag, } : undefined, + config.overAirVersion, ); }; @@ -324,11 +326,22 @@ function addOnFeatureUpdatedListener(config: InstabugConfig) { /** * Sets the Code Push version to be sent with each report. * @param version the Code Push version. + * + * @deprecated Use {@link setOverAirVersion} instead. */ export const setCodePushVersion = (version: string) => { NativeInstabug.setCodePushVersion(version); }; +/** + * Sets over air update version to be sent with each report. + * @param version the OTA version. + * + */ +export const setOverAirVersion = (OTAserviceVersion: OverAirUpdate) => { + NativeInstabug.setOverAirVersion(OTAserviceVersion); +}; + /** * Attaches user data to each report being sent. * Each call to this method overrides the user data to be attached. diff --git a/src/native/NativeConstants.ts b/src/native/NativeConstants.ts index f95634caf..22891ebf3 100644 --- a/src/native/NativeConstants.ts +++ b/src/native/NativeConstants.ts @@ -14,6 +14,7 @@ export type NativeConstants = NativeSdkDebugLogsLevel & NativeNonFatalErrorLevel & NativeStringKey & NativeLaunchType & + NativeOverAirUpdateServices & NativeAutoMaskingType & NativeUserConsentActionType; @@ -202,6 +203,10 @@ interface NativeLaunchType { unknown: any; } +interface NativeOverAirUpdateServices { + expo: any; + codePush: any; +} interface NativeAutoMaskingType { labels: any; textInputs: any; diff --git a/src/native/NativeInstabug.ts b/src/native/NativeInstabug.ts index c350d705f..1acecb5e4 100644 --- a/src/native/NativeInstabug.ts +++ b/src/native/NativeInstabug.ts @@ -14,6 +14,7 @@ import type { import type { NativeConstants } from './NativeConstants'; import type { W3cExternalTraceAttributes } from '../models/W3cExternalTraceAttributes'; import { NativeModules } from './NativePackage'; +import type { OverAirUpdate } from '../models/OverAirUpdate'; import type { ThemeConfig } from '../models/ThemeConfig'; export interface InstabugNativeModule extends NativeModule { @@ -31,11 +32,13 @@ export interface InstabugNativeModule extends NativeModule { options?: { ignoreAndroidSecureFlag?: boolean; }, + overAirVersion?: OverAirUpdate, ): void; show(): void; // Misc APIs // setCodePushVersion(version: string): void; + setOverAirVersion(OTAserviceVersion: OverAirUpdate): void; setAppVariant(appVariant: string): void; setIBGLogPrintsToConsole(printsToConsole: boolean): void; setSessionProfilerEnabled(isEnabled: boolean): void; diff --git a/src/utils/Enums.ts b/src/utils/Enums.ts index 1859ed2be..7044f49a2 100644 --- a/src/utils/Enums.ts +++ b/src/utils/Enums.ts @@ -250,6 +250,14 @@ export enum LaunchType { */ warm = constants.warm, } + +/** + * Over Air Update Service + */ +export enum OverAirUpdateServices { + codePush = constants.codePush, + expo = constants.expo, +} export enum AutoMaskingType { labels = constants.labels, textInputs = constants.textInputs, diff --git a/test/mocks/mockInstabug.ts b/test/mocks/mockInstabug.ts index e6f41fc73..01e1a59da 100644 --- a/test/mocks/mockInstabug.ts +++ b/test/mocks/mockInstabug.ts @@ -14,6 +14,7 @@ const mockInstabug: InstabugNativeModule = { setEnabled: jest.fn(), init: jest.fn(), setCodePushVersion: jest.fn(), + setOverAirVersion: jest.fn(), setUserData: jest.fn(), setTrackUserSteps: jest.fn(), setIBGLogPrintsToConsole: jest.fn(), diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index bd21b3efc..efae19f65 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -18,6 +18,7 @@ import { Locale, LogLevel, NetworkInterceptionMode, + OverAirUpdateServices, ReproStepsMode, StringKey, WelcomeMessageMode, @@ -295,6 +296,10 @@ describe('Instabug Module', () => { debugLogsLevel: LogLevel.debug, codePushVersion: '1.1.0', ignoreAndroidSecureFlag: true, + overAirVersion: { + service: OverAirUpdateServices.expo, + version: 'D0A12345-6789-4B3C-A123-4567ABCDEF01', + }, }; const usesNativeNetworkInterception = false; @@ -310,6 +315,7 @@ describe('Instabug Module', () => { instabugConfig.codePushVersion, undefined, { ignoreAndroidSecureFlag: instabugConfig.ignoreAndroidSecureFlag }, + instabugConfig.overAirVersion, ); }); @@ -322,6 +328,18 @@ describe('Instabug Module', () => { expect(NativeInstabug.setCodePushVersion).toBeCalledWith(codePushVersion); }); + it('setOverAirVersion should call native method setOverAirVersion', () => { + const OTAversion = { + service: OverAirUpdateServices.expo, + version: 'D0A12345-6789-4B3C-A123-4567ABCDEF01', + }; + + Instabug.setOverAirVersion(OTAversion); + + expect(NativeInstabug.setOverAirVersion).toBeCalledTimes(1); + expect(NativeInstabug.setOverAirVersion).toBeCalledWith(OTAversion); + }); + it('init should disable JavaScript interceptor when using native interception mode', () => { const instabugConfig = { token: 'some-token', @@ -330,6 +348,10 @@ describe('Instabug Module', () => { networkInterceptionMode: NetworkInterceptionMode.native, codePushVersion: '1.1.0', ignoreAndroidSecureFlag: true, + overAirVersion: { + service: OverAirUpdateServices.expo, + version: 'D0A12345-6789-4B3C-A123-4567ABCDEF01', + }, }; // Stubbing Network feature flags @@ -349,6 +371,7 @@ describe('Instabug Module', () => { // usesNativeNetworkInterception should be false when using native interception mode with Android false, instabugConfig.codePushVersion, + instabugConfig.overAirVersion, ); } else { expect(NativeInstabug.init).toBeCalledTimes(1); @@ -362,6 +385,7 @@ describe('Instabug Module', () => { instabugConfig.codePushVersion, undefined, { ignoreAndroidSecureFlag: instabugConfig.ignoreAndroidSecureFlag }, + instabugConfig.overAirVersion, ); } }); @@ -925,6 +949,10 @@ describe('Instabug iOS initialization tests', () => { debugLogsLevel: LogLevel.debug, networkInterceptionMode: NetworkInterceptionMode.native, codePushVersion: '1.1.0', + overAirVersion: { + service: OverAirUpdateServices.expo, + version: 'D0A12345-6789-4B3C-A123-4567ABCDEF01', + }, }; // Fast-forward until all timers have been executed jest.advanceTimersByTime(1000); @@ -945,6 +973,7 @@ describe('Instabug iOS initialization tests', () => { config.codePushVersion, config.ignoreAndroidSecureFlag, undefined, + config.overAirVersion, ); }); @@ -961,8 +990,9 @@ describe('Instabug iOS initialization tests', () => { config.debugLogsLevel, true, // Enable native interception config.codePushVersion, - config.ignoreAndroidSecureFlag, undefined, + undefined, + config.overAirVersion, ); }); @@ -981,6 +1011,7 @@ describe('Instabug iOS initialization tests', () => { config.codePushVersion, config.ignoreAndroidSecureFlag, undefined, + config.overAirVersion, ); }); @@ -1008,6 +1039,10 @@ describe('Instabug Android initialization tests', () => { debugLogsLevel: LogLevel.debug, networkInterceptionMode: NetworkInterceptionMode.javascript, codePushVersion: '1.1.0', + overAirVersion: { + service: OverAirUpdateServices.expo, + version: 'D0A12345-6789-4B3C-A123-4567ABCDEF01', + }, }; }); @@ -1023,8 +1058,9 @@ describe('Instabug Android initialization tests', () => { config.debugLogsLevel, false, // always disable native interception to insure sending network logs to core (Bugs & Crashes). config.codePushVersion, - undefined, { ignoreAndroidSecureFlag: config.ignoreAndroidSecureFlag }, + undefined, + config.overAirVersion, ); }); }); @@ -1106,6 +1142,7 @@ describe('Instabug Android initialization tests', () => { config.codePushVersion, config.appVariant, undefined, + config.overAirVersion, ); }); }); From 775f1d21efa743a27f91bb9f8ca2eb44e1e873dc Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:45:59 +0300 Subject: [PATCH 15/20] fix: CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 920652cd0..c7fc3d4d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Add Support Advanced UI customization. ([#1411](https://github.com/Instabug/Instabug-React-Native/pull/1411)) +- Add Support Eas updates. ([#1391](https://github.com/Instabug/Instabug-React-Native/pull/1391)) + ### Changed - **BREAKING** Remove deprecated APIs ([#1424](https://github.com/Instabug/Instabug-React-Native/pull/1424)). See migration guide for more details. From 5fc84a143924de9d136286ea4af872425457a9a1 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:31:28 +0300 Subject: [PATCH 16/20] feat: expo-sourcemap-support (#1392) * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: add expo sourcemap support * feat: update sourcemaps.gradle * feat: update sourcemaps.gradle * feat: update sourcemaps.gradle * feat: update sourcemaps.gradle * feat: update sourcemaps.gradle * feat: update sourcemaps.gradle * feat: add permissions * feat support sourcemap expo * feat support sourcemap expo * feat: add manifest permissions * feat: add manifest permissions * feat: add manifest permissions * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * fix: upload command * feat: expo sourcemap support * fix: CHANGELOG.md * fix: CHANGELOG.md --- CHANGELOG.md | 4 +- README.md | 16 ++- android/sourcemaps.gradle | 97 ++++++++++++++-- app.plugin.js | 1 + cli/commands/UploadEasUpdatesSourcemaps.ts | 36 ++++++ cli/index.ts | 4 +- cli/upload/index.ts | 1 + cli/upload/uploadEasUpdatesSourcemaps.ts | 74 ++++++++++++ .../InstabugExample.xcodeproj/project.pbxproj | 2 +- expo.d.ts | 1 + expo.js | 1 + ios/sourcemaps.sh | 2 +- package.json | 5 + plugin/src/index.ts | 5 + plugin/src/withInstabug.ts | 57 +++++++++ plugin/src/withInstabugAndroid.ts | 99 ++++++++++++++++ plugin/src/withInstabugIOS.ts | 109 ++++++++++++++++++ plugin/tsconfig.json | 7 ++ rollup.config.js | 8 ++ scripts/find-token.js | 58 ++++++++++ scripts/find-token.sh | 0 21 files changed, 570 insertions(+), 17 deletions(-) create mode 100644 app.plugin.js create mode 100755 cli/commands/UploadEasUpdatesSourcemaps.ts create mode 100644 cli/upload/uploadEasUpdatesSourcemaps.ts create mode 100644 expo.d.ts create mode 100644 expo.js create mode 100644 plugin/src/index.ts create mode 100644 plugin/src/withInstabug.ts create mode 100644 plugin/src/withInstabugAndroid.ts create mode 100644 plugin/src/withInstabugIOS.ts create mode 100644 plugin/tsconfig.json create mode 100755 scripts/find-token.js mode change 100644 => 100755 scripts/find-token.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index c7fc3d4d8..6b56beb07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Add Support Eas updates. ([#1391](https://github.com/Instabug/Instabug-React-Native/pull/1391)) +- Add support for uploading sourcemap files in expo apps. ([#1392](https://github.com/Instabug/Instabug-React-Native/pull/1392)) + ### Changed - **BREAKING** Remove deprecated APIs ([#1424](https://github.com/Instabug/Instabug-React-Native/pull/1424)). See migration guide for more details. @@ -36,8 +38,6 @@ ### Added -- Add support for expo updates versioning ([#1391](https://github.com/Instabug/Instabug-React-Native/pull/1391)) - - Add support enable/disable screenshot auto masking. ([#1389](https://github.com/Instabug/Instabug-React-Native/pull/1389)) - Add support for BugReporting user consents. ([#1383](https://github.com/Instabug/Instabug-React-Native/pull/1383)) diff --git a/README.md b/README.md index ded285e95..5fbf38c18 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,21 @@ For more info, visit [Instabug.com](https://www.instabug.com). yarn add instabug-reactnative ``` -2. CocoaPods on iOS needs this extra step: +2. if you are using expo you need to add `instabug-reactnative` plugin to `app.json`: + + ```json + "plugins" : [ + [ + "instabug-reactnative", + { + // optional that add Mic,Photo permission on iOS and FOREGROUND_SERVICE_MEDIA_PROJECTION on android + "addScreenRecordingBugReportingPermission": true + } + ] + ] + ``` + +3. CocoaPods on iOS needs this extra step: ```bash cd ios && pod install && cd .. diff --git a/android/sourcemaps.gradle b/android/sourcemaps.gradle index 5558c01ba..e5b2fb9db 100644 --- a/android/sourcemaps.gradle +++ b/android/sourcemaps.gradle @@ -15,11 +15,13 @@ project.afterEvaluate { def flavor = name.substring(start, end).uncapitalize() def defaultVersion = getDefaultVersion(flavor) - task.finalizedBy createUploadSourcemapsTask(flavor, defaultVersion.name, defaultVersion.code) + + + task.finalizedBy createUploadSourcemapsTask(flavor, defaultVersion.name, defaultVersion.code,task) } } -Task createUploadSourcemapsTask(String flavor, String defaultVersionName, String defaultVersionCode) { +Task createUploadSourcemapsTask(String flavor, String defaultVersionName, String defaultVersionCode, Task task) { def name = 'uploadSourcemaps' + flavor.capitalize() // Don't recreate the task if it already exists. @@ -39,18 +41,26 @@ Task createUploadSourcemapsTask(String flavor, String defaultVersionName, String try { def appProject = project(':app') def appDir = appProject.projectDir - def sourceMapFile = getSourceMapFile(appDir, flavor) + def sourceMapFile = getSourceMapFile(appDir, flavor,task) + println "✅ Resolved sourcemap file path: ${sourceMapFile.absolutePath}" def jsProjectDir = rootDir.parentFile def instabugDir = new File(['node', '-p', 'require.resolve("instabug-reactnative/package.json")'].execute(null, rootDir).text.trim()).getParentFile() - def tokenShellFile = new File(instabugDir, 'scripts/find-token.sh') - def inferredToken = executeShellScript(tokenShellFile, jsProjectDir) + def tokenJsFile = new File(instabugDir, 'scripts/find-token.js') + def inferredToken = executeNodeScript(tokenJsFile, jsProjectDir) + + if (!inferredToken) { + throw new GradleException("❌ Unable to infer Instabug token from script: ${tokenShellFile.absolutePath}") + } + def appToken = resolveVar('App Token', 'INSTABUG_APP_TOKEN', inferredToken) def versionName = resolveVar('Version Name', 'INSTABUG_VERSION_NAME', defaultVersionName) def versionCode = resolveVar('Version Code', 'INSTABUG_VERSION_CODE', defaultVersionCode) + println "📦 Uploading with versionName=${versionName}, versionCode=${versionCode}, appToken=${appToken.take(5)}..." + exec { def osCompatibility = Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c'] : [] def args = [ @@ -67,6 +77,8 @@ Task createUploadSourcemapsTask(String flavor, String defaultVersionName, String } catch (exception) { project.logger.error "Failed to upload source map file.\n" + "Reason: ${exception.message}" + throw exception + } } } @@ -74,27 +86,48 @@ Task createUploadSourcemapsTask(String flavor, String defaultVersionName, String return provider.get() } -File getSourceMapFile(File appDir, String flavor) { +File getSourceMapFile(File appDir, String flavor, Task task) { def defaultFlavorPath = flavor.empty ? 'release' : "${flavor}Release" def defaultSourceMapDest = "build/generated/sourcemaps/react/${defaultFlavorPath}/index.android.bundle.map" def defaultSourceMapFile = new File(appDir, defaultSourceMapDest) + def props = task.getProperties() + + def bundleAssetName = props.containsKey("bundleAssetName") ? props.bundleAssetName?.getOrNull() : null + def jsSourceMapsDir = props.containsKey("jsSourceMapsDir") ? props.jsSourceMapsDir?.getOrNull() : null + def jsIntermediateSourceMapsDir = props.containsKey("jsIntermediateSourceMapsDir") ? props.jsIntermediateSourceMapsDir?.getOrNull() : null + + if (bundleAssetName && jsSourceMapsDir) { + def outputSourceMap = new File(jsSourceMapsDir.asFile.absolutePath, "${bundleAssetName}.map") + if (outputSourceMap.exists()) { + return outputSourceMap + } + } + + if (bundleAssetName && jsIntermediateSourceMapsDir) { + def packagerOutputSourceMap = new File(jsIntermediateSourceMapsDir.asFile.absolutePath, "${bundleAssetName}.packager.map") + if (packagerOutputSourceMap.exists()) { + return packagerOutputSourceMap + } + } if (defaultSourceMapFile.exists()) { return defaultSourceMapFile } if (flavor.empty) { - throw new InvalidUserDataException("Unable to find source map file at: ${defaultSourceMapFile.absolutePath}.") + println"Source map file not found at: ${defaultSourceMapFile.absolutePath}. Skipping." + return null } def fallbackSourceMapDest = "build/generated/sourcemaps/react/${flavor}/release/index.android.bundle.map" def fallbackSourceMapFile = new File(appDir, fallbackSourceMapDest) - project.logger.info "Unable to find source map file at: ${defaultSourceMapFile.absolutePath}.\n" + + println "Unable to find source map file at: ${defaultSourceMapFile.absolutePath}.\n" + "Falling back to ${fallbackSourceMapFile.absolutePath}." if (!fallbackSourceMapFile.exists()) { - throw new InvalidUserDataException("Unable to find source map file at: ${fallbackSourceMapFile.absolutePath} either.") + println "Fallback source map file not found at: ${fallbackSourceMapFile.absolutePath}. Skipping." + return null } return fallbackSourceMapFile @@ -155,14 +188,56 @@ String resolveVar(String name, String envKey, String defaultValue) { return value } +static String executeNodeScript(File script, File workingDir) { + if (!script.exists()) { + println "Script not found: ${script.absolutePath}" + return null + } + + def output = new StringBuffer() + def error = new StringBuffer() + + try { + def process = ['node', script.getAbsolutePath()].execute(null, workingDir) + process.waitForProcessOutput(output, error) + + if (process.exitValue() != 0) { + println "Script failed with exit code ${process.exitValue()}" + println "Standard Error:\n${error.toString().trim()}" + println "Standard Output:\n${output.toString().trim()}" + return null + } + + return output.toString().trim() + + } catch (Exception e) { + println "Exception while executing Node.js script: ${e.message}" + e.printStackTrace() + return null + } +} + static String executeShellScript(File script, File workingDir) { if (Os.isFamily(Os.FAMILY_WINDOWS)) { return null } + if (!script.canExecute()) { + // Try to set executable permission + script.setExecutable(true) + } + def output = new StringBuffer() + def error = new StringBuffer() + + // Using 'sh' instead of './' to avoid needing exec permission, but keeping chmod above just in case def process = ['sh', script.getAbsolutePath()].execute(null, workingDir) - process?.waitForProcessOutput(output, new StringBuffer()) + process?.waitForProcessOutput(output, error) + + if (process.exitValue() != 0) { + println "Error running script: ${error.toString().trim()}" + return null + } - return process?.exitValue() == 0 ? output.toString().trim() : null + return output.toString().trim() } diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 000000000..3ae4fa6e4 --- /dev/null +++ b/app.plugin.js @@ -0,0 +1 @@ +module.exports = require('./expo'); diff --git a/cli/commands/UploadEasUpdatesSourcemaps.ts b/cli/commands/UploadEasUpdatesSourcemaps.ts new file mode 100755 index 000000000..4a39eb769 --- /dev/null +++ b/cli/commands/UploadEasUpdatesSourcemaps.ts @@ -0,0 +1,36 @@ +import { Command, Option } from 'commander'; +import { UploadEasUpdatesSourcemaps, UploadEasUpdatesSourcemapsOptions } from '../upload'; + +export const uploadEasUpdatesSourcemapsCommand = new Command(); + +uploadEasUpdatesSourcemapsCommand + .name('upload-eas-updates-sourcemaps') + .addOption( + new Option('-f, --file ', 'The path of eas update folder') + .makeOptionMandatory() + .default('dist'), + ) + .addOption( + new Option('-t, --token ', 'Your App Token') + .env('INSTABUG_APP_TOKEN') + .makeOptionMandatory(), + ) + .addOption( + new Option('-n, --name ', 'The app version name') + .env('INSTABUG_APP_VERSION_NAME') + .makeOptionMandatory(), + ) + .addOption( + new Option('-c, --code ', 'The app version code') + .env('INSTABUG_APP_VERSION_CODE') + .makeOptionMandatory(), + ) + .addOption( + new Option('--androidUpdateId ', "The CodePush label if it's a CodePush release"), + ) + .addOption(new Option('--iosUpdateId ', "The CodePush label if it's a CodePush release")) + .action(function (this: Command) { + const options = this.opts(); + UploadEasUpdatesSourcemaps(options); + }) + .showHelpAfterError(); diff --git a/cli/index.ts b/cli/index.ts index 8df747e75..43ac14a9b 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -3,6 +3,7 @@ import { Command } from 'commander'; import { uploadSourcemapsCommand } from './commands/UploadSourcemaps'; import { UploadSoFilesCommand } from './commands/UploadSoFiles'; +import { uploadEasUpdatesSourcemapsCommand } from './commands/UploadEasUpdatesSourcemaps'; const program = new Command(); @@ -12,6 +13,7 @@ program .description('A CLI for uploading source maps to Instabug dashboard.') .usage('[command]') .addCommand(uploadSourcemapsCommand) - .addCommand(UploadSoFilesCommand); + .addCommand(UploadSoFilesCommand) + .addCommand(uploadEasUpdatesSourcemapsCommand); program.parse(process.argv); diff --git a/cli/upload/index.ts b/cli/upload/index.ts index b09f4b243..7a2dc8c1a 100644 --- a/cli/upload/index.ts +++ b/cli/upload/index.ts @@ -1,2 +1,3 @@ export * from './uploadSourcemaps'; export * from './uploadSoFiles'; +export * from './uploadEasUpdatesSourcemaps'; diff --git a/cli/upload/uploadEasUpdatesSourcemaps.ts b/cli/upload/uploadEasUpdatesSourcemaps.ts new file mode 100644 index 000000000..12ef00ed4 --- /dev/null +++ b/cli/upload/uploadEasUpdatesSourcemaps.ts @@ -0,0 +1,74 @@ +import fs from 'fs'; +import { uploadSourcemaps } from './uploadSourcemaps'; +import * as path from 'path'; + +export interface UploadEasUpdatesSourcemapsOptions { + file: string; + token: string; + name: string; + code: string; + androidUpdateId?: string; + iosUpdateId?: string; + /** + * Disables logging to the console and prevents process exit on error. + * + * @default false + * */ + silent?: boolean; +} + +function getMapFile(folderPath: string): string | null { + try { + if (fs.existsSync(folderPath)) { + const files = fs.readdirSync(folderPath); + const mapFile = files.find((file) => file.endsWith('.map')); + if (!mapFile) { + return null; + } + return path.join(folderPath, mapFile); + } + return null; + } catch (err) { + console.error('Failed to read folder:', err); + return null; + } +} + +/** + * Uploads JavaScript sourcemaps to Instabug. + * + * @param opts Options for the sourcemaps upload process. + * @returns A promise that resolves to a boolean indicating whether the upload was successful. + */ +export const UploadEasUpdatesSourcemaps = async ( + opts: UploadEasUpdatesSourcemapsOptions, +): Promise => { + const jsFolderPath = path.join(opts.file, '_expo', 'static', 'js'); + + const androidFile = getMapFile(path.join(jsFolderPath, 'android')); + const iosFile = getMapFile(path.join(jsFolderPath, 'ios')); + if (androidFile && fs.existsSync(androidFile)) { + await uploadSourcemaps({ + platform: 'android', + name: opts.name, + code: opts.code, + token: opts.token, + label: opts.androidUpdateId, + file: androidFile, + silent: opts.silent, + }); + } + + if (iosFile && fs.existsSync(iosFile)) { + await uploadSourcemaps({ + platform: 'ios', + name: opts.name, + code: opts.code, + token: opts.token, + label: opts.iosUpdateId, + file: iosFile, + silent: opts.silent, + }); + } + return true; +}; diff --git a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj index 0778e9a59..5ccd8c8b7 100644 --- a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj +++ b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj @@ -430,7 +430,7 @@ name = "[CP-User] [instabug-reactnative] Upload Sourcemap"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\n\n\nexport SOURCEMAP_FILE=\"$DERIVED_FILE_DIR/main.jsbundle.map\"\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n\nlocal sourcemap_file=\"\"\n # Use existing sourcemap if available\n if [[ -f \"$SOURCEMAP_FILE\" ]]; then\n sourcemap_file=\"$SOURCEMAP_FILE\"\n else\n sourcemap_file=$(generate_sourcemaps | tail -n 1)\nfi\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\nif [ -n \"$sourcemap_file\" ]; then\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $sourcemap_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n fi\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir\n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ -n \"$default_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; + shellScript = "#!/bin/sh\n\n\nexport SOURCEMAP_FILE=\"$DERIVED_FILE_DIR/main.jsbundle.map\"\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n\nlocal sourcemap_file=\"\"\n # Use existing sourcemap if available\n if [[ -f \"$SOURCEMAP_FILE\" ]]; then\n sourcemap_file=\"$SOURCEMAP_FILE\"\n else\n sourcemap_file=$(generate_sourcemaps | tail -n 1)\nfi\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && node $instabug_dir/scripts/find-token.js)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\nif [ -n \"$sourcemap_file\" ]; then\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $sourcemap_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n fi\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir\n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ -n \"$default_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; }; B77A7BA143DBD17E8AAFD0B4 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/expo.d.ts b/expo.d.ts new file mode 100644 index 000000000..020de8e4b --- /dev/null +++ b/expo.d.ts @@ -0,0 +1 @@ +export * from './plugin/build'; diff --git a/expo.js b/expo.js new file mode 100644 index 000000000..3c7d11b61 --- /dev/null +++ b/expo.js @@ -0,0 +1 @@ +module.exports = require('./plugin/build'); diff --git a/ios/sourcemaps.sh b/ios/sourcemaps.sh index 6b9eee7df..134eb90b1 100644 --- a/ios/sourcemaps.sh +++ b/ios/sourcemaps.sh @@ -42,7 +42,7 @@ fi local js_project_dir="$PROJECT_DIR/.." local instabug_dir=$(dirname $(node -p "require.resolve('instabug-reactnative/package.json')")) - local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh) + local inferred_token=$(cd $js_project_dir && node $instabug_dir/scripts/find-token.js) local app_token=$(resolve_var "App Token" "INSTABUG_APP_TOKEN" "$inferred_token" | tail -n 1) local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' "$PROJECT_DIR/$INFOPLIST_FILE") diff --git a/package.json b/package.json index ed64a12c7..69139ecee 100644 --- a/package.json +++ b/package.json @@ -80,5 +80,10 @@ "typescript": "^4.8.4", "wait-for-expect": "^3.0.2", "xhr2": "^0.2.1" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } } } diff --git a/plugin/src/index.ts b/plugin/src/index.ts new file mode 100644 index 000000000..e701c4217 --- /dev/null +++ b/plugin/src/index.ts @@ -0,0 +1,5 @@ +import { withInstabug } from './withInstabug'; + +export { withInstabug }; + +export default withInstabug; diff --git a/plugin/src/withInstabug.ts b/plugin/src/withInstabug.ts new file mode 100644 index 000000000..d4eff3ad5 --- /dev/null +++ b/plugin/src/withInstabug.ts @@ -0,0 +1,57 @@ +import type { ConfigPlugin } from 'expo/config-plugins'; +import { createRunOncePlugin } from 'expo/config-plugins'; + +import { withInstabugAndroid } from './withInstabugAndroid'; +import { withInstabugIOS } from './withInstabugIOS'; + +export interface PluginProps { + name?: string; + forceUploadSourceMaps?: boolean; + addScreenRecordingBugReportingPermission?: boolean; + addBugReportingIosMediaPermission?: boolean; +} + +const instabugPackage = require('../../package.json') as { + name: string; + version: string; +}; + +const withInstabugPlugin: ConfigPlugin = (config, props = {}) => { + const { + forceUploadSourceMaps = false, + addScreenRecordingBugReportingPermission = false, + addBugReportingIosMediaPermission = true, + } = props; + + const sharedProps = { + ...props, + name: instabugPackage.name, + forceUploadSourceMaps, + addScreenRecordingBugReportingPermission, + addBugReportingIosMediaPermission, + }; + + let updatedConfig = config; + + // Android configuration (only if source maps are enabled) + try { + updatedConfig = withInstabugAndroid(updatedConfig, sharedProps); + } catch (err) { + console.warn('[Instabug] Failed to configure Android project:', (err as Error).message ?? err); + } + + // iOS configuration + try { + updatedConfig = withInstabugIOS(updatedConfig, sharedProps); + } catch (err) { + console.warn('[Instabug] Failed to configure iOS project:', (err as Error).message ?? err); + } + + return updatedConfig; +}; + +export const withInstabug = createRunOncePlugin( + withInstabugPlugin, + instabugPackage.name, + instabugPackage.version, +); diff --git a/plugin/src/withInstabugAndroid.ts b/plugin/src/withInstabugAndroid.ts new file mode 100644 index 000000000..e827beb70 --- /dev/null +++ b/plugin/src/withInstabugAndroid.ts @@ -0,0 +1,99 @@ +import type { ConfigPlugin } from 'expo/config-plugins'; +import { withAppBuildGradle, withAndroidManifest } from 'expo/config-plugins'; +import type { PluginProps } from './withInstabug'; + +export const withInstabugAndroid: ConfigPlugin = (config, props) => { + config = withAppBuildGradle(config, (configAndroid) => { + if (props.forceUploadSourceMaps) { + const gradle = configAndroid.modResults; + const packageName = props.name; + + if (!packageName) { + console.warn('[Instabug] Missing "name" in plugin props. Skipping Android configuration.'); + return configAndroid; + } + + if (gradle.language === 'groovy') { + gradle.contents = injectGroovyScript(gradle.contents, packageName); + } else if (gradle.language === 'kt') { + gradle.contents = injectKotlinScript(gradle.contents, packageName); + } else { + throw new Error( + '[Instabug] Unsupported Gradle language. Only Groovy and Kotlin DSL are supported.', + ); + } + } + return configAndroid; + }); + + // Inject the permission if requested + if (props.addScreenRecordingBugReportingPermission) { + config = withAndroidManifest(config, (configAndroid) => { + const manifest = configAndroid.modResults; + + const permissionName = 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION'; + const alreadyExists = manifest.manifest['uses-permission']?.some( + (permission: any) => permission.$?.['android:name'] === permissionName, + ); + + if (!alreadyExists) { + manifest.manifest['uses-permission'] = [ + ...(manifest.manifest['uses-permission'] || []), + { + $: { + 'android:name': permissionName, + }, + }, + ]; + } + + return configAndroid; + }); + } + + return config; +}; + +// --- Helper Functions --- + +function injectGroovyScript(buildGradle: string, packageName: string): string { + if (buildGradle.includes('sourcemaps.gradle')) { + return buildGradle; + } + + const androidBlockPattern = /^android\s*{/m; + if (!androidBlockPattern.test(buildGradle)) { + console.warn('[Instabug] Could not find "android {" block in Groovy build.gradle.'); + return buildGradle; + } + + const script = ` +def instabugPath = ["node", "--print", "require('path').dirname(require.resolve('${packageName}/package.json'))"] + .execute() + .text + .trim() +apply from: new File(instabugPath, "android/sourcemaps.gradle") +`.trim(); + + return buildGradle.replace(androidBlockPattern, `${script}\n\nandroid {`); +} + +function injectKotlinScript(buildGradle: string, packageName: string): string { + if (buildGradle.includes('sourcemaps.gradle')) { + return buildGradle; + } + + const androidBlockPattern = /^android\s*{/m; + if (!androidBlockPattern.test(buildGradle)) { + console.warn('[Instabug] Could not find "android {" block in Kotlin build.gradle.kts.'); + return buildGradle; + } + + const script = ` +val instabugPath = listOf("node", "--print", "require('path').dirname(require.resolve("${packageName}/package.json"))") + .let { ProcessBuilder(it).start().inputStream.bufferedReader().readText().trim() } +apply(from = File(instabugPath, "android/sourcemaps.gradle")) +`.trim(); + + return buildGradle.replace(androidBlockPattern, `${script}\n\nandroid {`); +} diff --git a/plugin/src/withInstabugIOS.ts b/plugin/src/withInstabugIOS.ts new file mode 100644 index 000000000..0815e0f26 --- /dev/null +++ b/plugin/src/withInstabugIOS.ts @@ -0,0 +1,109 @@ +import type { ConfigPlugin, XcodeProject } from 'expo/config-plugins'; +import { withXcodeProject, withInfoPlist } from 'expo/config-plugins'; +import type { PluginProps } from './withInstabug'; +import * as path from 'path'; +import * as fs from 'fs'; + +const BUILD_PHASE = 'PBXShellScriptBuildPhase'; +const PHASE_COMMENT = 'Bundle React Native code and images'; +const INSTABUG_BUILD_PHASE = '[instabug-reactnative] Upload Sourcemap'; + +export const withInstabugIOS: ConfigPlugin = (config, props) => { + let updatedConfig = withXcodeProject(config, (configXcode) => { + const xcodeProject = configXcode.modResults; + const buildPhases = xcodeProject.hash.project.objects[BUILD_PHASE]; + + if (!buildPhases) { + console.warn('[Instabug] No build phases found in Xcode project.'); + return configXcode; + } + + // Add Instabug build phase if not already present + const hasInstabugPhase = Boolean(findBuildPhase(buildPhases, INSTABUG_BUILD_PHASE)); + + if (!hasInstabugPhase && props.forceUploadSourceMaps) { + addInstabugBuildPhase(xcodeProject, props.name); + } + + // Patch bundle React Native phase with source map export + const bundlePhase = xcodeProject.pbxItemByComment(PHASE_COMMENT, BUILD_PHASE); + if (bundlePhase?.shellScript) { + bundlePhase.shellScript = injectSourceMapExport(bundlePhase.shellScript); + } + + return configXcode; + }); + + // Add media permissions to Info.plist if enabled + if (props.addBugReportingIosMediaPermission) { + const instabugConfig = config.extra?.instabug ?? {}; + + const microphonePermission = + instabugConfig.microphonePermission || + 'This needs access to your microphone so you can attach voice notes.'; + + const photoLibraryPermission = + instabugConfig.photoLibraryPermission || + 'This needs access to your photo library so you can attach images.'; + + updatedConfig = withInfoPlist(updatedConfig, (configXcode) => { + const plist = configXcode.ios.infoPlist ?? {}; + + if (!plist.NSMicrophoneUsageDescription) { + plist.NSMicrophoneUsageDescription = microphonePermission; + } + + if (!plist.NSPhotoLibraryUsageDescription) { + plist.NSPhotoLibraryUsageDescription = photoLibraryPermission; + } + + configXcode.ios.infoPlist = plist; + return configXcode; + }); + } + + return updatedConfig; +}; + +// Find a build phase by its clean name +function findBuildPhase(buildPhases: any, targetName: string): any | undefined { + const target = targetName.toLowerCase().trim(); + return Object.values(buildPhases).find((phase: any) => { + const rawName = phase?.name ?? ''; + const cleanName = rawName + .toLowerCase() + .replace('[cp-user] ', '') + .replace(/^"+|"+$/g, '') + .trim(); + return cleanName === target; + }); +} + +// Inject Instabug shell script phase +function addInstabugBuildPhase(xcodeProject: XcodeProject, packageName: string): void { + try { + const packagePath = require.resolve(`${packageName}/package.json`); + const sourcemapScriptPath = path.join(path.dirname(packagePath), 'ios/sourcemaps.sh'); + + if (!fs.existsSync(sourcemapScriptPath)) { + console.warn(`[Instabug] sourcemaps.sh not found at: ${sourcemapScriptPath}`); + return; + } + + xcodeProject.addBuildPhase([], BUILD_PHASE, INSTABUG_BUILD_PHASE, null, { + shellPath: '/bin/sh', + shellScript: `/bin/sh ${sourcemapScriptPath}`, + }); + } catch (err) { + console.warn(`[Instabug] Failed to resolve package path for "${packageName}":`, err); + } +} + +// Inject source map export line into the shell script +function injectSourceMapExport(script: string): string { + const exportLine = 'export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"'; + const escapedLine = exportLine.replace(/\$/g, '\\$').replace(/"/g, '\\"'); + const injectedLine = `${escapedLine}\\n`; + + return script.includes(escapedLine) ? script : script.replace(/^"/, `"${injectedLine}`); +} diff --git a/plugin/tsconfig.json b/plugin/tsconfig.json new file mode 100644 index 000000000..6b7ac0e4d --- /dev/null +++ b/plugin/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "outDir": "build", + "rootDir": "src" + }, + "include": ["./src"] +} diff --git a/rollup.config.js b/rollup.config.js index ae91a8906..62bb06ced 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -23,6 +23,14 @@ export default [ }, plugins: [...commonPlugins, typescript({ tsconfig: './tsconfig.cli.json' })], }, + { + input: ['plugin/src/index.ts'], + output: { + dir: 'plugin/build', + format: 'cjs', + }, + plugins: [...commonPlugins, typescript({ tsconfig: './plugin/tsconfig.json' })], + }, { input: ['cli/upload/index.ts'], output: { diff --git a/scripts/find-token.js b/scripts/find-token.js new file mode 100755 index 000000000..1a69dca60 --- /dev/null +++ b/scripts/find-token.js @@ -0,0 +1,58 @@ +const fs = require('fs'); +const path = require('path'); + +const IGNORED_DIRS = new Set(['node_modules', 'ios', 'android']); +const INCLUDED_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx']); + +function getAllFiles(dir, fileList = []) { + const files = fs.readdirSync(dir); + for (const file of files) { + const fullPath = path.join(dir, file); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + if (!IGNORED_DIRS.has(file)) { + getAllFiles(fullPath, fileList); + } + } else { + if (INCLUDED_EXTENSIONS.has(path.extname(fullPath))) { + fileList.push(fullPath); + } + } + } + return fileList; +} + +function extractTokenFromInit(content) { + const initMatch = content.match(/Instabug\.init\(\s*{[\s\S]*?token:\s*['"]([0-9a-zA-Z]+)['"]/); + return initMatch ? initMatch[1] : null; +} + +function extractTokenFromStart(content) { + const startMatch = content.match(/Instabug\.start\(\s*['"]([0-9a-zA-Z]+)['"]/); + return startMatch ? startMatch[1] : null; +} + +function findInstabugToken() { + const allFiles = getAllFiles('.'); + + for (const file of allFiles) { + const content = fs.readFileSync(file, 'utf-8'); + + const initToken = extractTokenFromInit(content); + if (initToken) { + console.log(initToken); + process.exit(0); + } + + const startToken = extractTokenFromStart(content); + if (startToken) { + console.log(startToken); + process.exit(0); + } + } + + console.log("Couldn't find Instabug's app token"); + process.exit(1); +} + +findInstabugToken(); diff --git a/scripts/find-token.sh b/scripts/find-token.sh old mode 100644 new mode 100755 From dc906f1d97d9a3110a6dbd1c5cd46a43f648c605 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:42:34 +0300 Subject: [PATCH 17/20] fix: expo-plugin (#1438) * fix: expo plugin * fix: expo plugin * fix: expo plugin * fix: expo plugin * fix: expo plugin * fix: expo plugin * fix: expo plugin * fix: expo plugin --- .circleci/config.yml | 3 - package.json | 3 +- src/models/OverAirUpdate.ts | 2 +- yarn.lock | 361 ++++++++++++++++++++++++++++++++++-- 4 files changed, 350 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index afebbdeaf..2ba5d1653 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,9 +14,6 @@ references: - validate_shell_files - sync_generated_files - test_ios - - e2e_ios - - e2e_android - dream11_prepare_config: &dream11_prepare_config prepare_steps: - prepare_dream11 diff --git a/package.json b/package.json index 69139ecee..1e3f32515 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "@types/jest": "^29.5.3", "@types/node": "^20.4.8", "@types/react-native": "^0.72.2", - "axios": "^1.2.2", + "axios": "1.11.0", + "@expo/config-plugins": "7.8.4", "babel-core": "7.0.0-bridge.0", "babel-jest": "^29.6.2", "commander": "^11.0.0", diff --git a/src/models/OverAirUpdate.ts b/src/models/OverAirUpdate.ts index dd7de040f..7808445fe 100644 --- a/src/models/OverAirUpdate.ts +++ b/src/models/OverAirUpdate.ts @@ -1,4 +1,4 @@ -import { OverAirUpdateServices } from '../utils/Enums'; +import type { OverAirUpdateServices } from '../utils/Enums'; export interface OverAirUpdate { /** * the name of OTA service diff --git a/yarn.lock b/yarn.lock index 7d8e53b23..d5cf44ade 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,6 +41,13 @@ dependencies: "@babel/highlight" "^7.22.5" +"@babel/code-frame@~7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" @@ -254,6 +261,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.25.9": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" @@ -277,6 +289,16 @@ "@babel/traverse" "^7.22.6" "@babel/types" "^7.22.5" +"@babel/highlight@^7.10.4": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6" + integrity sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" @@ -855,6 +877,77 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== +"@expo/config-plugins@7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.4.tgz#533b5d536c1dc8b5544d64878b51bda28f2e1a1f" + integrity sha512-hv03HYxb/5kX8Gxv/BTI8TLc9L06WzqAfHRRXdbar4zkLcP2oTzvsLEF4/L/TIpD3rsnYa0KU42d0gWRxzPCJg== + dependencies: + "@expo/config-types" "^50.0.0-alpha.1" + "@expo/fingerprint" "^0.6.0" + "@expo/json-file" "~8.3.0" + "@expo/plist" "^0.1.0" + "@expo/sdk-runtime-versions" "^1.0.0" + "@react-native/normalize-color" "^2.0.0" + chalk "^4.1.2" + debug "^4.3.1" + find-up "~5.0.0" + getenv "^1.0.0" + glob "7.1.6" + resolve-from "^5.0.0" + semver "^7.5.3" + slash "^3.0.0" + slugify "^1.6.6" + xcode "^3.0.1" + xml2js "0.6.0" + +"@expo/config-types@^50.0.0-alpha.1": + version "50.0.1" + resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-50.0.1.tgz#12d889214dedf64fbf2322c9d9e75c9d5ca7f695" + integrity sha512-EZHMgzkWRB9SMHO1e9m8s+OMahf92XYTnsCFjxhSfcDrcEoSdFPyJWDJVloHZPMGhxns7Fi2+A+bEVN/hD4NKA== + +"@expo/fingerprint@^0.6.0": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.6.1.tgz#763ae79b06f60e10853596bfa2bd730bfb13f2b0" + integrity sha512-ggLn6unI6qowlA1FihdQwPpLn16VJulYkvYAEL50gaqVahfNEglRQMSH2giZzjD0d6xq2/EQuUdFyHaJfyJwOQ== + dependencies: + "@expo/spawn-async" "^1.5.0" + chalk "^4.1.2" + debug "^4.3.4" + find-up "^5.0.0" + minimatch "^3.0.4" + p-limit "^3.1.0" + resolve-from "^5.0.0" + +"@expo/json-file@~8.3.0": + version "8.3.3" + resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.3.3.tgz#7926e3592f76030ce63d6b1308ac8f5d4d9341f4" + integrity sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A== + dependencies: + "@babel/code-frame" "~7.10.4" + json5 "^2.2.2" + write-file-atomic "^2.3.0" + +"@expo/plist@^0.1.0": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.1.3.tgz#b4fbee2c4f7a88512a4853d85319f4d95713c529" + integrity sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg== + dependencies: + "@xmldom/xmldom" "~0.7.7" + base64-js "^1.2.3" + xmlbuilder "^14.0.0" + +"@expo/sdk-runtime-versions@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c" + integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ== + +"@expo/spawn-async@^1.5.0": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58" + integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew== + dependencies: + cross-spawn "^7.0.3" + "@gitbeaker/core@^21.7.0": version "21.7.0" resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-21.7.0.tgz#fcf7a12915d39f416e3f316d0a447a814179b8e5" @@ -1563,6 +1656,11 @@ resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.72.1.tgz#905343ef0c51256f128256330fccbdb35b922291" integrity sha512-cRPZh2rBswFnGt5X5EUEPs0r+pAsXxYsifv/fgy9ZLQokuT52bPH+9xjDR+7TafRua5CttGW83wP4TntRcWNDA== +"@react-native/normalize-color@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" + integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== + "@react-native/normalize-colors@*": version "0.73.0" resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.73.0.tgz#23e15cf2a2b73ac7e5e6df8d5b86b173cfb35a3f" @@ -2032,6 +2130,16 @@ dependencies: tslib "^2.3.0" +"@xmldom/xmldom@^0.8.8": + version "0.8.11" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608" + integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw== + +"@xmldom/xmldom@~0.7.7": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.13.tgz#ff34942667a4e19a9f4a0996a76814daac364cf3" + integrity sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2265,13 +2373,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== +axios@1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.11.0.tgz#c2ec219e35e414c025b2095e8b8280278478fdb6" + integrity sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA== dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" + follow-redirects "^1.15.6" + form-data "^4.0.4" proxy-from-env "^1.1.0" babel-core@7.0.0-bridge.0, babel-core@^7.0.0-bridge.0: @@ -2413,7 +2521,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.1.2, base64-js@^1.3.1: +base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -2423,6 +2531,11 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== +big-integer@1.6.x: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + big-integer@^1.6.44: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" @@ -2437,6 +2550,20 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +bplist-creator@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== + dependencies: + stream-buffers "2.2.x" + +bplist-parser@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" + integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== + dependencies: + big-integer "1.6.x" + bplist-parser@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" @@ -2543,6 +2670,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -2590,7 +2725,7 @@ caniuse-lite@^1.0.30001517: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== -chalk@^2.0.0, chalk@^2.3.0: +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2912,6 +3047,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +debug@^4.3.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3052,6 +3194,15 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -3163,6 +3314,23 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.10" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -3172,6 +3340,16 @@ es-set-tostringtag@^2.0.1: has "^1.0.3" has-tostringtag "^1.0.0" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -3616,7 +3794,7 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@^5.0.0: +find-up@^5.0.0, find-up@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -3652,10 +3830,10 @@ flow-parser@^0.206.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.206.0.tgz#f4f794f8026535278393308e01ea72f31000bfef" integrity sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w== -follow-redirects@^1.15.0: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +follow-redirects@^1.15.6: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== for-each@^0.3.3: version "0.3.3" @@ -3682,6 +3860,17 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -3716,6 +3905,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -3751,11 +3945,35 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -3781,6 +3999,11 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +getenv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/getenv/-/getenv-1.0.0.tgz#874f2e7544fbca53c7a4738f37de8605c3fcfc31" + integrity sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg== + git-config-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/git-config-path/-/git-config-path-1.0.1.tgz#6d33f7ed63db0d0e118131503bab3aca47d54664" @@ -3804,6 +4027,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3865,6 +4100,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + got@^11.1.4: version "11.8.6" resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" @@ -3936,6 +4176,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -3943,6 +4188,13 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3950,6 +4202,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hermes-estree@0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.12.0.tgz#8a289f9aee854854422345e6995a48613bac2ca8" @@ -5190,6 +5449,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + memfs-or-file-map-to-github-branch@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz#fdb9a85408262316a9bd5567409bf89be7d72f96" @@ -5609,7 +5873,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1: +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -6076,6 +6340,15 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +plist@^3.0.5: + version "3.1.0" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" + integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + pod-install@^0.1.38: version "0.1.38" resolved "https://registry.yarnpkg.com/pod-install/-/pod-install-0.1.38.tgz#1c16a800a5fc1abea0cafcc0e190f376368c76ab" @@ -6608,6 +6881,11 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + scheduler@0.24.0-canary-efb381bbf-20230505: version "0.24.0-canary-efb381bbf-20230505" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz#5dddc60e29f91cd7f8b983d7ce4a99c2202d178f" @@ -6721,6 +6999,15 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-plist@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" + integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== + dependencies: + bplist-creator "0.1.0" + bplist-parser "0.3.1" + plist "^3.0.5" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -6745,6 +7032,11 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slugify@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" + integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -6838,6 +7130,11 @@ statuses@~1.5.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +stream-buffers@2.2.x: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -7297,6 +7594,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-to-istanbul@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" @@ -7444,11 +7746,42 @@ xcase@^2.0.1: resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== +xcode@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" + integrity sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== + dependencies: + simple-plist "^1.1.0" + uuid "^7.0.3" + xhr2@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.2.1.tgz#4e73adc4f9cfec9cbd2157f73efdce3a5f108a93" integrity sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw== +xml2js@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.0.tgz#07afc447a97d2bd6507a1f76eeadddb09f7a8282" + integrity sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-14.0.0.tgz#876b5aec4f05ffd5feb97b0a871c855d16fbeb8c" + integrity sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg== + +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From 34b0066b2e34da6b761ce5f79fc407530fb507da Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:56:33 +0300 Subject: [PATCH 18/20] chore: bump ios SDK to v16.0.2 --- examples/default/ios/Podfile.lock | 6 +++--- ios/native.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 0e43c84ef..81343178a 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -31,7 +31,7 @@ PODS: - hermes-engine (0.75.4): - hermes-engine/Pre-built (= 0.75.4) - hermes-engine/Pre-built (0.75.4) - - Instabug (16.0.1) + - Instabug (16.0.2) - instabug-reactnative-ndk (0.1.0): - DoubleConversion - glog @@ -1626,7 +1626,7 @@ PODS: - ReactCommon/turbomodule/core - Yoga - RNInstabug (16.0.0): - - Instabug (= 16.0.1) + - Instabug (= 16.0.2) - React-Core - RNReanimated (3.16.1): - DoubleConversion @@ -2022,7 +2022,7 @@ SPEC CHECKSUMS: Google-Maps-iOS-Utils: f77eab4c4326d7e6a277f8e23a0232402731913a GoogleMaps: 032f676450ba0779bd8ce16840690915f84e57ac hermes-engine: ea92f60f37dba025e293cbe4b4a548fd26b610a0 - Instabug: f31a27e70cb6580aef656d1abf8c57a5bb5633d0 + Instabug: 125f729dea4e4a43e815ae06f9db0332e2a5fd60 instabug-reactnative-ndk: d765ac289d56e8896398d02760d9abf2562fc641 OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74 RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740 diff --git a/ios/native.rb b/ios/native.rb index 6c1848522..c351a93c3 100644 --- a/ios/native.rb +++ b/ios/native.rb @@ -1,4 +1,4 @@ -$instabug = { :version => '16.0.1' } +$instabug = { :version => '16.0.2' } def use_instabug! (spec = nil) version = $instabug[:version] From 9db3bb86462e89f9268fce56f544c8eb578709a8 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:59:45 +0300 Subject: [PATCH 19/20] chore: bump ios SDK to v16.0.2 --- examples/default/ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index 81343178a..8e27864fd 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -2090,7 +2090,7 @@ SPEC CHECKSUMS: ReactCommon: 6a952e50c2a4b694731d7682aaa6c79bc156e4ad RNCClipboard: 2821ac938ef46f736a8de0c8814845dde2dcbdfb RNGestureHandler: 511250b190a284388f9dd0d2e56c1df76f14cfb8 - RNInstabug: 6cf5149b0b50ad7d6806d761fc2014ce27bae779 + RNInstabug: f06cf043f071311456d3ad14b4f9da628f2cd29b RNReanimated: f42a5044d121d68e91680caacb0293f4274228eb RNScreens: c7ceced6a8384cb9be5e7a5e88e9e714401fd958 RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d From 1f352d43ed214142305231d9b3910eeddf65d68f Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 20 Aug 2025 19:57:27 +0300 Subject: [PATCH 20/20] chore: bump ios SDK to v16.0.2 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b56beb07..9f892abcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - **BREAKING** Remove deprecated APIs ([#1424](https://github.com/Instabug/Instabug-React-Native/pull/1424)). See migration guide for more details. -- Bump Instabug iOS SDK to v16.0.1 ([#1436](https://github.com/Instabug/Instabug-React-Native/pull/1436)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/16.0.1). +- Bump Instabug iOS SDK to v16.0.2 ([#1436](https://github.com/Instabug/Instabug-React-Native/pull/1436)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/16.0.1). - Bump Instabug Android SDK to v16.0.0 ([#1436](https://github.com/Instabug/Instabug-React-Native/pull/1436)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v16.0.1).