diff --git a/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleCrashReportingModule.java b/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleCrashReportingModule.java
index 4ccb7ad3c..d10291dd3 100644
--- a/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleCrashReportingModule.java
+++ b/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleCrashReportingModule.java
@@ -28,11 +28,10 @@ public String getName() {
}
@ReactMethod
- public void sendNativeNonFatal(final String exceptionObject) {
+ public void sendNativeNonFatal() {
final IBGNonFatalException exception = new IBGNonFatalException.Builder(new IllegalStateException("Test exception"))
.build();
CrashReporting.report(exception);
-
}
@ReactMethod
diff --git a/examples/default/package.json b/examples/default/package.json
index 7e6bc4c4e..528486455 100644
--- a/examples/default/package.json
+++ b/examples/default/package.json
@@ -14,6 +14,7 @@
"@react-native-community/netinfo": "^11.4.1",
"@react-native-community/slider": "^4.5.5",
"@react-navigation/bottom-tabs": "^6.5.7",
+ "@react-navigation/material-top-tabs": "6.5.3",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"axios": "^1.7.4",
@@ -28,10 +29,12 @@
"react-native-config": "^1.5.3",
"react-native-gesture-handler": "^2.13.4",
"react-native-maps": "1.10.3",
+ "react-native-pager-view": "^6.9.1",
"react-native-reanimated": "^3.16.1",
"react-native-safe-area-context": "^4.12.0",
"react-native-screens": "^3.35.0",
"react-native-svg": "^15.8.0",
+ "react-native-tab-view": "^3.5.2",
"react-native-vector-icons": "^10.2.0",
"react-native-webview": "^13.13.2",
"react-query": "^3.39.3"
diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx
index ceef8bc19..5b3435fc8 100644
--- a/examples/default/src/App.tsx
+++ b/examples/default/src/App.tsx
@@ -21,6 +21,7 @@ import { nativeBaseTheme } from './theme/nativeBaseTheme';
import { navigationTheme } from './theme/navigationTheme';
import { QueryClient, QueryClientProvider } from 'react-query';
+import { CallbackHandlersProvider } from './contexts/callbackContext';
const queryClient = new QueryClient();
@@ -89,7 +90,9 @@ export const App: React.FC = () => {
-
+
+
+
diff --git a/examples/default/src/components/InputField.tsx b/examples/default/src/components/InputField.tsx
index feef8fd7b..f3db99ed3 100644
--- a/examples/default/src/components/InputField.tsx
+++ b/examples/default/src/components/InputField.tsx
@@ -21,6 +21,7 @@ interface InputFieldProps {
maxLength?: number;
accessibilityLabel?: string;
flex?: number;
+ testID?: string;
}
export const InputField = forwardRef(
@@ -34,6 +35,7 @@ export const InputField = forwardRef(
maxLength,
keyboardType,
errorText,
+ testID,
...restProps
},
ref,
@@ -43,11 +45,14 @@ export const InputField = forwardRef(
@@ -63,9 +68,10 @@ const styles = StyleSheet.create({
borderWidth: 1,
borderColor: '#ccc',
paddingVertical: 10,
- paddingHorizontal: 24,
- fontSize: 16,
+ paddingHorizontal: 16,
+ fontSize: 12,
borderRadius: 5,
+ color: 'black',
},
errorText: {
color: '#ff0000',
diff --git a/examples/default/src/components/ListTile.tsx b/examples/default/src/components/ListTile.tsx
index 35540dc85..97b9146f7 100644
--- a/examples/default/src/components/ListTile.tsx
+++ b/examples/default/src/components/ListTile.tsx
@@ -1,25 +1,47 @@
import React, { PropsWithChildren } from 'react';
-import { Box, HStack, Pressable, Text } from 'native-base';
+import { Box, HStack, Pressable, Text, VStack } from 'native-base';
interface ListTileProps extends PropsWithChildren {
title: string;
+ subtitle?: string;
onPress?: () => void;
+ testID?: string;
+ truncateSubtitle?: boolean;
+ testID?: string;
}
-export const ListTile: React.FC = ({ title, onPress, children }) => {
+export const ListTile: React.FC = ({ title,
+ subtitle,
+ onPress,
+ children,
+ testID,
+ truncateSubtitle = false, }) => {
return (
- {title}
+
+ {title}
+ {subtitle && (
+
+ {subtitle}
+
+ )}
+
{children}
diff --git a/examples/default/src/components/PlatformListTile.tsx b/examples/default/src/components/PlatformListTile.tsx
index 1bf1fe6e7..bbee40444 100644
--- a/examples/default/src/components/PlatformListTile.tsx
+++ b/examples/default/src/components/PlatformListTile.tsx
@@ -7,6 +7,7 @@ interface PlatformListTileProps extends PropsWithChildren {
title: string;
onPress?: () => void;
platform?: 'ios' | 'android';
+ testID?: string;
}
export const PlatformListTile: React.FC = ({
@@ -14,6 +15,7 @@ export const PlatformListTile: React.FC = ({
onPress,
platform,
children,
+ testID,
}) => {
if (Platform.OS === platform || !platform) {
return (
@@ -25,7 +27,8 @@ export const PlatformListTile: React.FC = ({
borderBottomWidth="1"
borderColor="coolGray.300"
bg="coolGray.100"
- _pressed={{ bg: 'coolGray.200' }}>
+ _pressed={{ bg: 'coolGray.200' }}
+ testID={testID}>
{title}
{children}
diff --git a/examples/default/src/components/Select.tsx b/examples/default/src/components/Select.tsx
index 206aa74e0..de5e6a8fe 100644
--- a/examples/default/src/components/Select.tsx
+++ b/examples/default/src/components/Select.tsx
@@ -6,15 +6,17 @@ interface SelectItem {
label: string;
value: T;
isInitial?: boolean;
+ testID?: string;
}
interface SelectProps {
label: string;
items: SelectItem[];
onValueChange: (value: T) => void;
+ testID?: string;
}
-export function Select({ label, items, onValueChange }: SelectProps) {
+export function Select({ label, items, onValueChange, testID }: SelectProps) {
const initialItem = items.find((i) => i.isInitial) ?? items[0];
const [selectedItem, setSelectedItem] = useState(initialItem);
@@ -35,7 +37,12 @@ export function Select({ label, items, onValueChange }: SelectProps) {
endIcon: ,
}}>
{items.map((item) => (
-
+
))}
);
diff --git a/examples/default/src/contexts/callbackContext.tsx b/examples/default/src/contexts/callbackContext.tsx
new file mode 100644
index 000000000..eaa431feb
--- /dev/null
+++ b/examples/default/src/contexts/callbackContext.tsx
@@ -0,0 +1,56 @@
+import React, { createContext, useContext, useState } from 'react';
+
+// A single key/value pair
+export type KeyValuePair = { key: string; value: string };
+
+// An item that contains multiple key/value pairs
+export type Item = {
+ id: string;
+ fields: KeyValuePair[]; // list of key/value pairs
+};
+
+// CallbackHandlersType = { [title: string]: Item[] }
+export type CallbackHandlersType = Record;
+
+type CallbackHandlersContextType = {
+ callbackHandlers: CallbackHandlersType;
+ clearList: (title: string) => void;
+ addItem: (title: string, item: Item) => void;
+};
+
+const CallbackHandlersContext = createContext(undefined);
+
+export const CallbackHandlersProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+ const [callbackHandlers, setCallbackHandlers] = useState({});
+
+ // Clears all items under a specific title
+ const clearList = (title: string) => {
+ setCallbackHandlers((prev) => ({ ...prev, [title]: [] }));
+ };
+
+ // Adds an item (with multiple key/value pairs) to a specific title list
+ const addItem = (title: string, item: Item) => {
+ setCallbackHandlers((prev) => {
+ const existingList = prev[title] || [];
+ return {
+ ...prev,
+ [title]: [...existingList, item],
+ };
+ });
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+// Hook to use the context
+export const useCallbackHandlers = () => {
+ const ctx = useContext(CallbackHandlersContext);
+ if (!ctx) {
+ throw new Error('useCallbackHandlers must be used within CallbackHandlersProvider');
+ }
+ return ctx;
+};
diff --git a/examples/default/src/navigation/HomeStack.tsx b/examples/default/src/navigation/HomeStack.tsx
index 090aa6587..ab2b8d4b6 100644
--- a/examples/default/src/navigation/HomeStack.tsx
+++ b/examples/default/src/navigation/HomeStack.tsx
@@ -2,8 +2,41 @@ import React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
-import { BugReportingScreen } from '../screens/BugReportingScreen';
+import { BugReportingScreen } from '../screens/bug-reporting/BugReportingScreen';
+import {
+ BugReportingStateScreen,
+ type BugReportingStateScreenProp,
+} from '../screens/bug-reporting/BugReportingStateScreen';
+import {
+ ExtendedBugReportStateScreen,
+ type ExtendedBugReportStateScreenProp,
+} from '../screens/bug-reporting/ExtendedBugReportStateScreen';
+import {
+ BugReportingTypesScreen,
+ type BugReportingTypesScreenProp,
+} from '../screens/bug-reporting/BugReportingTypesScreen';
+import {
+ DisclaimerTextScreen,
+ type DisclaimerTextScreenProp,
+} from '../screens/bug-reporting/DisclaimerTextScreen';
+import {
+ InvocationOptionsScreen,
+ type InvocationOptionsScreenProp,
+} from '../screens/bug-reporting/InvocationOptionsScreen';
+import {
+ ViewHierarchyScreen,
+ type ViewHierarchyScreenProp,
+} from '../screens/bug-reporting/ViewHierarchyScreen';
+import {
+ RepliesStateScreen,
+ type RepliesStateScreenProp,
+} from '../screens/bug-reporting/RepliesStateScreen';
+import { UserConsentScreen } from '../screens/bug-reporting/UserConsentScreen';
import { CrashReportingScreen } from '../screens/CrashReportingScreen';
+import {
+ CrashReportingStateScreen,
+ type CrashReportingStateScreenProp,
+} from '../screens/crash-reporting/CrashReportingStateScreen';
import { FeatureRequestsScreen } from '../screens/FeatureRequestsScreen';
import { HomeScreen } from '../screens/HomeScreen';
import { RepliesScreen } from '../screens/RepliesScreen';
@@ -23,7 +56,7 @@ 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 { NetworkScreen } from '../screens/apm/network/NetworkScreen';
import { FlowsScreen } from '../screens/apm/FlowsScreen';
import { SessionReplayScreen } from '../screens/SessionReplayScreen';
import { LegacyModeScreen } from '../screens/LegacyModeScreen';
@@ -31,11 +64,53 @@ import { HttpScreen } from '../screens/apm/HttpScreen';
import { WebViewsScreen } from '../screens/apm/webViews/WebViewsScreen';
import { FullWebViewsScreen } from '../screens/apm/webViews/FullWebViewsScreen';
import { PartialWebViewsScreen } from '../screens/apm/webViews/PartialWebViewsScreen';
+import {
+ InvocationEventsScreen,
+ type InvocationEventsScreenProp,
+} from '../screens/bug-reporting/InvocationEventsScreen';
+import {
+ SessionProfilerScreen,
+ type SessionProfilerScreenProp,
+} from '../screens/bug-reporting/SessionProfilerScreen';
+import {
+ NDKCrashesStateScreen,
+ type NDKCrashesStateScreenProp,
+} from '../screens/crash-reporting/NDKCrashesStateScreen';
+import { NonFatalCrashesScreen } from '../screens/crash-reporting/NonFatalCrashesScreen';
+import { FatalCrashesScreen } from '../screens/crash-reporting/FatalCrashesScreen';
+import CallbackScreen from '../screens/CallbackHandlersScreen';
+import {
+ NetworkStateScreen,
+ type NetworkStateScreenProp,
+} from '../screens/apm/network/NetworkStateScreen';
+import {
+ UserStepsStateScreen,
+ type UserStepsStateScreenProp,
+} from '../screens/settings/UserStepsStateScreen';
export type HomeStackParamList = {
Home: undefined;
+
+ // Bug Reporting //
BugReporting: undefined;
+ BugReportingState: BugReportingStateScreenProp;
+ ExtendedBugReportState: ExtendedBugReportStateScreenProp;
+ BugReportingTypes: BugReportingTypesScreenProp;
+ DisclaimerText: DisclaimerTextScreenProp;
+ InvocationEvents: InvocationEventsScreenProp;
+ SessionProfiler: SessionProfilerScreenProp;
+ InvocationOptions: InvocationOptionsScreenProp;
+ ViewHierarchy: ViewHierarchyScreenProp;
+ RepliesState: RepliesStateScreenProp;
+ UserConsent: undefined;
+
+ // Crash Reporting //
CrashReporting: undefined;
+ CrashReportingState: CrashReportingStateScreenProp;
+ NDKCrashesState: NDKCrashesStateScreenProp;
+ NonFatalCrashes: undefined;
+ FatalCrashes: undefined;
+
FeatureRequests: undefined;
Replies: undefined;
Surveys: undefined;
@@ -61,6 +136,10 @@ export type HomeStackParamList = {
WebViews: undefined;
FullWebViews: undefined;
PartialWebViews: undefined;
+ NetworkState: NetworkStateScreenProp;
+ UserStepsState: UserStepsStateScreenProp;
+
+ CallbackScreen: undefined;
};
const HomeStack = createNativeStackNavigator();
@@ -69,16 +148,91 @@ export const HomeStackNavigator: React.FC = () => {
return (
+
+ {/* Bug Reporting */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* Crash Reporting */}
+
+
+
+
+
{
component={PartialWebViewsScreen}
options={{ title: 'PartialWebViews' }}
/>
+
+
+
+
);
};
diff --git a/examples/default/src/navigation/RootTab.tsx b/examples/default/src/navigation/RootTab.tsx
index f085576a9..9f2487503 100644
--- a/examples/default/src/navigation/RootTab.tsx
+++ b/examples/default/src/navigation/RootTab.tsx
@@ -6,7 +6,7 @@ import {
} from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
-import { SettingsScreen } from '../screens/SettingsScreen';
+import { SettingsScreen } from '../screens/settings/SettingsScreen';
import { HomeStackNavigator } from './HomeStack';
import { Platform } from 'react-native';
diff --git a/examples/default/src/screens/BugReportingScreen.tsx b/examples/default/src/screens/BugReportingScreen.tsx
deleted file mode 100644
index a8afc14a8..000000000
--- a/examples/default/src/screens/BugReportingScreen.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React from 'react';
-
-import Instabug, {
- BugReporting,
- InvocationOption,
- ReportType,
- ExtendedBugReportMode,
- WelcomeMessageMode,
-} from 'instabug-reactnative';
-
-import { ListTile } from '../components/ListTile';
-import { Screen } from '../components/Screen';
-import { useToast } from 'native-base';
-import { Section } from '../components/Section';
-
-export const BugReportingScreen: React.FC = () => {
- const toast = useToast();
- return (
-
- Instabug.show()} />
- BugReporting.show(ReportType.bug, [])} />
- BugReporting.show(ReportType.feedback, [InvocationOption.emailFieldHidden])}
- />
- BugReporting.show(ReportType.question, [])} />
-
- BugReporting.setExtendedBugReportMode(ExtendedBugReportMode.enabledWithRequiredFields)
- }
- />
-
- BugReporting.setExtendedBugReportMode(ExtendedBugReportMode.enabledWithOptionalFields)
- }
- />
- Instabug.setSessionProfilerEnabled(true)}
- />
- Instabug.showWelcomeMessage(WelcomeMessageMode.beta)}
- />
- Instabug.showWelcomeMessage(WelcomeMessageMode.live)}
- />
-
-
-
- BugReporting.onInvokeHandler(function () {
- Instabug.appendTags(['Invocation Handler tag1']);
- })
- }
- />
-
- Instabug.onReportSubmitHandler(() => {
- toast.show({
- description: 'Submission succeeded',
- });
- })
- }
- />
-
- BugReporting.onSDKDismissedHandler(function () {
- Instabug.setPrimaryColor('#FF0000');
- })
- }
- />
-
-
- );
-};
diff --git a/examples/default/src/screens/CallbackHandlersScreen.tsx b/examples/default/src/screens/CallbackHandlersScreen.tsx
new file mode 100644
index 000000000..086e89fdc
--- /dev/null
+++ b/examples/default/src/screens/CallbackHandlersScreen.tsx
@@ -0,0 +1,109 @@
+import React from 'react';
+import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
+import { useCallbackHandlers } from '../contexts/callbackContext';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+type CallBackScreenProps = { title: string };
+
+const CallbackScreen: React.FC = () => {
+ const { callbackHandlers } = useCallbackHandlers();
+ const titles = Object.keys(callbackHandlers);
+ const Tab = createMaterialTopTabNavigator();
+
+ return (
+
+ {titles.length > 0 ? (
+ titles.map((title) => (
+
+ {() => }
+
+ ))
+ ) : (
+ (
+ No Data
+ ),
+ }}>
+ {() => (
+
+ No callback handlers yet
+
+ )}
+
+ )}
+
+ );
+};
+
+const CallBackTabScreen: React.FC = ({ title }) => {
+ const { callbackHandlers, clearList } = useCallbackHandlers();
+ const items = callbackHandlers[title] || [];
+
+ return (
+
+
+ Items: {items.length}
+ clearList(title)}>
+ Clear Data
+
+
+
+ `${item.id}-${index}`}
+ renderItem={({ item }) => (
+
+ {item.fields.map((field, idx) => (
+
+ {field.key}:
+ {field.value}
+
+ ))}
+
+ )}
+ ListEmptyComponent={No items}
+ />
+
+ );
+};
+
+export default CallbackScreen;
+
+const styles = StyleSheet.create({
+ container: { flex: 1, padding: 16, backgroundColor: '#fff' },
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ countText: { fontSize: 16, fontWeight: 'bold' },
+ clearButton: { backgroundColor: '#ff5555', padding: 8, borderRadius: 8 },
+ clearText: { color: '#fff', fontWeight: 'bold' },
+ item: {
+ padding: 12,
+ backgroundColor: '#f0f0f0',
+ borderRadius: 6,
+ marginBottom: 8,
+ },
+ fieldRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 4,
+ },
+ keyText: { fontWeight: 'bold', color: '#333' },
+ valueText: { color: '#555', marginLeft: 8 },
+ empty: { textAlign: 'center', color: '#888', marginTop: 20 },
+ emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
+ emptyText: { fontSize: 16, color: '#888' },
+});
diff --git a/examples/default/src/screens/CrashReportingScreen.tsx b/examples/default/src/screens/CrashReportingScreen.tsx
index 397565ecd..b30f2cb5b 100644
--- a/examples/default/src/screens/CrashReportingScreen.tsx
+++ b/examples/default/src/screens/CrashReportingScreen.tsx
@@ -1,296 +1,72 @@
import React, { useState } from 'react';
-import { Alert, Platform, ScrollView, StyleSheet, Text, View, Switch } from 'react-native';
+import { Platform, ScrollView } from 'react-native';
-import { CrashReporting, NonFatalErrorLevel } from 'instabug-reactnative';
+import { CrashReporting } from 'instabug-reactnative';
import { ListTile } from '../components/ListTile';
import { Screen } from '../components/Screen';
-import { Section } from '../components/Section';
-import { PlatformListTile } from '../components/PlatformListTile';
-import { NativeExampleCrashReporting } from '../native/NativeCrashReporting';
-import { VerticalListTile } from '../components/VerticalListTile';
-import { Button, VStack } from 'native-base';
-import { InputField } from '../components/InputField';
-import { Select } from '../components/Select';
-import { showNotification } from '../utils/showNotification';
+import { Divider } from 'native-base';
-const styles = StyleSheet.create({
- inputWrapper: {
- padding: 4,
- flex: 1,
- },
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../navigation/HomeStack';
- formContainer: {
- flexDirection: 'row',
- alignItems: 'stretch',
- },
-});
-
-export const CrashReportingScreen: React.FC = () => {
- function throwHandledException(error: Error) {
- try {
- if (!error.message) {
- const appName = 'Instabug Test App';
- error.message = `Handled ${error.name} From ${appName}`;
- }
- throw error;
- } catch (err) {
- if (err instanceof Error) {
- CrashReporting.reportError(err, { level: NonFatalErrorLevel.critical }).then(() =>
- Alert.alert(`Crash report for ${error.name} is Sent!`),
- );
- }
- }
- }
-
- function throwUnhandledException(error: Error, isPromise: boolean = false) {
- const appName = 'Instabug Test App';
- const rejectionType = isPromise ? 'Promise Rejection ' : '';
- const errorMessage = `Unhandled ${rejectionType}${error.name} from ${appName}`;
-
- if (!error.message) {
- console.log(`IBG-CRSH | Error message: ${error.message}`);
- error.message = errorMessage;
- }
-
- if (isPromise) {
- console.log('IBG-CRSH | Promise');
- Promise.reject(error).then(() =>
- Alert.alert(`Promise Rejection Crash report for ${error.name} is Sent!`),
- );
- } else {
- throw error;
- }
- }
- const [isEnabled, setIsEnabled] = useState(false);
-
- const [userAttributeKey, setUserAttributeKey] = useState('');
- const [userAttributeValue, setUserAttributeValue] = useState('');
- const [crashNameValue, setCrashNameValue] = useState('');
- const [crashFingerprint, setCrashFingerprint] = useState('');
- const [crashLevelValue, setCrashLevelValue] = useState(
- NonFatalErrorLevel.error,
- );
-
- const toggleSwitch = (value: boolean) => {
- setIsEnabled(value);
- CrashReporting.setEnabled(value);
- showNotification('Crash Reporting status', 'Crash Reporting enabled set to ' + value);
- };
-
- function sendCrash() {
- try {
- const error = new Error(crashNameValue);
-
- throw error;
- } catch (err) {
- if (err instanceof Error) {
- const attrMap: { [k: string]: string } = {};
- attrMap[userAttributeKey] = userAttributeValue;
-
- const userAttributes: Record = {};
- if (userAttributeKey && userAttributeValue) {
- userAttributes[userAttributeKey] = userAttributeValue;
- }
- const fingerprint = crashFingerprint.length === 0 ? undefined : crashFingerprint;
-
- CrashReporting.reportError(err, {
- userAttributes: userAttributes,
- fingerprint: fingerprint,
- level: crashLevelValue,
- }).then(() => {
- Alert.alert(`Crash report for ${crashNameValue} is Sent!`);
- });
- }
- }
- }
+export const CrashReportingScreen: React.FC<
+ NativeStackScreenProps
+> = ({ navigation }) => {
+ const [isEnabled, setIsEnabled] = useState(true);
+ const [isNDKEnabled, setIsNDKEnabled] = useState(true);
return (
-
- Crash Reporting Enabled:
-
-
-
+
+
+ {
+ navigation.navigate('CrashReportingState', {
+ isEnabled,
+ setIsEnabled: (enabled: boolean) => {
+ setIsEnabled(enabled);
+ CrashReporting.setEnabled(enabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_cr_state"
+ />
+
+ {Platform.OS === 'android' && (
throwHandledException(new Error())}
+ title="NDK Crashes State"
+ subtitle={isNDKEnabled ? 'Enabled' : 'Disabled'}
+ onPress={() => {
+ navigation.navigate('NDKCrashesState', {
+ isEnabled: isNDKEnabled,
+ setIsEnabled: (enabled: boolean) => {
+ setIsNDKEnabled(enabled);
+ CrashReporting.setNDKCrashesEnabled(enabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_ndk_cr_state"
/>
- throwHandledException(new SyntaxError())}
- />
- throwHandledException(new RangeError())}
- />
- throwHandledException(new ReferenceError())}
- />
- throwHandledException(new URIError())}
- />
- NativeExampleCrashReporting.sendNativeNonFatal()}
- />
-
-
-
- setCrashNameValue(key)}
- value={crashNameValue}
- />
-
-
-
- setUserAttributeKey(key)}
- value={userAttributeKey}
- />
-
-
- setUserAttributeValue(value)}
- value={userAttributeValue}
- />
-
-
-
-
-
-
- setCrashFingerprint(text)}
- value={crashFingerprint}
- />
-
-
-
-
-
-
- Fatal Crashes can only be tested in release mode
- These buttons will crash the application.
- throwUnhandledException(Error(), true)}
- />
- throwUnhandledException(Error())}
- />
- throwUnhandledException(new SyntaxError())}
- />
- throwUnhandledException(new RangeError())}
- />
- throwUnhandledException(new ReferenceError())}
- />
- throwUnhandledException(new URIError())}
- />
- NativeExampleCrashReporting.sendNativeFatalCrash()}
- />
- NativeExampleCrashReporting.sendFatalHang()}
- />
- NativeExampleCrashReporting.sendANR()}
- platform={'android'}
- />
- NativeExampleCrashReporting.sendOOM()}
- />
-
- {Platform.OS === 'android' ? (
-
- NDK Crashes can only be tested in release mode
- These buttons will crash the application.
- {
- console.log('Sending NDK SIGSEGV Crash');
- await NativeExampleCrashReporting.causeSIGSEGVCrash();
- }}
- />
- {
- console.log('Sending NDK SIGFPE Crash');
- await NativeExampleCrashReporting.causeSIGFPECrash();
- }}
- />
- {
- console.log('Sending NDK SIGILL Crash');
- await NativeExampleCrashReporting.causeSIGILLCrash();
- }}
- />
-
- {
- console.log('Sending NDK SIGBUS Crash');
- await NativeExampleCrashReporting.causeSIGBUSCrash();
- }}
- />
- {
- console.log('Sending NDK SIGTRAP Crash');
- await NativeExampleCrashReporting.causeSIGTRAPCrash();
- }}
- />
-
- ) : (
-
)}
-
-
+
+
+
+ navigation.navigate('NonFatalCrashes')}
+ testID="id_non_fatal_crashes"
+ />
+
+ navigation.navigate('FatalCrashes')}
+ testID="id_fatal_crashes"
+ />
+
+
);
};
diff --git a/examples/default/src/screens/HomeScreen.tsx b/examples/default/src/screens/HomeScreen.tsx
index 7a5aeeb46..012db7020 100644
--- a/examples/default/src/screens/HomeScreen.tsx
+++ b/examples/default/src/screens/HomeScreen.tsx
@@ -20,6 +20,7 @@ export const HomeScreen: React.FC navigation.navigate('APM')} />
navigation.navigate('SessionReplay')} />
navigation.navigate('LegacyMode')} />
+ navigation.navigate('CallbackScreen')} />
);
};
diff --git a/examples/default/src/screens/SessionReplayScreen.tsx b/examples/default/src/screens/SessionReplayScreen.tsx
index 3a3d49ed9..9c7729779 100644
--- a/examples/default/src/screens/SessionReplayScreen.tsx
+++ b/examples/default/src/screens/SessionReplayScreen.tsx
@@ -1,13 +1,24 @@
-import React from 'react';
+import React, { useState } from 'react';
import { SessionReplay } from 'instabug-reactnative';
import { useToast } from 'native-base';
import { ListTile } from '../components/ListTile';
import { Screen } from '../components/Screen';
+import { UserStepsState } from './settings/UserStepsStateScreen';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../navigation/HomeStack';
-export const SessionReplayScreen: React.FC = () => {
+export const SessionReplayScreen: React.FC<
+ NativeStackScreenProps
+> = ({ navigation }) => {
const toast = useToast();
+
+ const [isSessionReplayEnabled, setIsSessionReplayEnabled] = useState(true);
+ const [isSessionNetworkLogsEnabled, setIsSessionNetworkLogsEnabled] = useState(true);
+ const [isSessionInstabugLogsEnabled, setIsSessionInstabugLogsEnabled] = useState(true);
+ const [isSessionUserStepsEnabled, setIsSessionUSerStepsEnabled] = useState(true);
+
return (
{
}
}}
/>
+
+ {
+ navigation.navigate('UserStepsState', {
+ state: isSessionReplayEnabled ? UserStepsState.Enabled : UserStepsState.Disabled,
+ setState: (newState: UserStepsState) => {
+ const isEnabled = newState === UserStepsState.Enabled;
+ setIsSessionReplayEnabled(isEnabled);
+ SessionReplay.setEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_steps_replay_state"
+ />
+
+ {
+ navigation.navigate('UserStepsState', {
+ state: isSessionNetworkLogsEnabled ? UserStepsState.Enabled : UserStepsState.Disabled,
+ setState: (newState: UserStepsState) => {
+ const isEnabled = newState === UserStepsState.Enabled;
+ setIsSessionNetworkLogsEnabled(isEnabled);
+ SessionReplay.setNetworkLogsEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_steps_replay_network_state"
+ />
+
+ {
+ navigation.navigate('UserStepsState', {
+ state: isSessionInstabugLogsEnabled ? UserStepsState.Enabled : UserStepsState.Disabled,
+ setState: (newState: UserStepsState) => {
+ const isEnabled = newState === UserStepsState.Enabled;
+ setIsSessionInstabugLogsEnabled(isEnabled);
+ SessionReplay.setInstabugLogsEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_steps_replay_instabug_lgos_state"
+ />
+
+ {
+ navigation.navigate('UserStepsState', {
+ state: isSessionUserStepsEnabled ? UserStepsState.Enabled : UserStepsState.Disabled,
+ setState: (newState: UserStepsState) => {
+ const isEnabled = newState === UserStepsState.Enabled;
+ setIsSessionUSerStepsEnabled(isEnabled);
+ SessionReplay.setUserStepsEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_steps_replay_usersteps_state"
+ />
);
};
diff --git a/examples/default/src/screens/apm/HttpScreen.tsx b/examples/default/src/screens/apm/HttpScreen.tsx
index 48f972ae4..61b02ac0a 100644
--- a/examples/default/src/screens/apm/HttpScreen.tsx
+++ b/examples/default/src/screens/apm/HttpScreen.tsx
@@ -156,12 +156,12 @@ export const HttpScreen: React.FC = () => {
return (
{loading && }
-
-
-
-
-
-
+
+
+
+
+
+
);
};
diff --git a/examples/default/src/screens/apm/NetworkScreen.tsx b/examples/default/src/screens/apm/network/NetworkScreen.tsx
similarity index 68%
rename from examples/default/src/screens/apm/NetworkScreen.tsx
rename to examples/default/src/screens/apm/network/NetworkScreen.tsx
index 4225e6185..accab6d86 100644
--- a/examples/default/src/screens/apm/NetworkScreen.tsx
+++ b/examples/default/src/screens/apm/network/NetworkScreen.tsx
@@ -1,16 +1,20 @@
import React, { useState } from 'react';
import { Image, ScrollView, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
-import { Section } from '../../components/Section';
-import { Screen } from '../../components/Screen';
-import { ClipboardTextInput } from '../../components/ClipboardTextInput';
+import { Section } from '../../../components/Section';
+import { Screen } from '../../../components/Screen';
+import { ClipboardTextInput } from '../../../components/ClipboardTextInput';
import { useQuery } from 'react-query';
import { HStack, VStack } from 'native-base';
import { gql, GraphQLClient } from 'graphql-request';
-import { CustomButton } from '../../components/CustomButton';
+import { CustomButton } from '../../../components/CustomButton';
import axios from 'axios';
-import type { HomeStackParamList } from '../../navigation/HomeStack';
+import type { HomeStackParamList } from '../../../navigation/HomeStack';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useNetInfo } from '@react-native-community/netinfo';
+import { ListTile } from '../../../components/ListTile';
+import { NetworkLogger } from 'instabug-reactnative';
+import { NetworkState } from './NetworkStateScreen';
+import { useCallbackHandlers } from '../../../contexts/callbackContext';
export const NetworkScreen: React.FC<
NativeStackScreenProps
@@ -27,6 +31,7 @@ export const NetworkScreen: React.FC<
'https://fastly.picsum.photos/id/57/200/300.jpg?hmac=l908G1qVr4r7dP947-tak2mY8Vvic_vEYzCXUCKKskY',
'https://fastly.picsum.photos/id/619/200/300.jpg?hmac=WqBGwlGjuY9RCdpzRaG9G-rc9Fi7TGUINX_-klAL2kA',
];
+ const { addItem } = useCallbackHandlers();
async function sendRequestToUrl() {
let urlToSend: string;
@@ -153,6 +158,7 @@ export const NetworkScreen: React.FC<
throw error;
}
}
+
async function makeParallelApiCalls(urls: string[]): Promise {
const fetchPromises = urls.map((url) => fetch(url).then((response) => response.json()));
@@ -164,9 +170,27 @@ export const NetworkScreen: React.FC<
}
}
+ const [isNetworkEnabled, setIsNetworkEnabled] = useState(true);
+
return (
+ {
+ navigation.navigate('NetworkState', {
+ state: isNetworkEnabled ? NetworkState.Enabled : NetworkState.Disabled,
+ setState: (newState: NetworkState) => {
+ const isEnabled = newState === NetworkState.Enabled;
+ setIsNetworkEnabled(isEnabled);
+ NetworkLogger.setEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_network_state"
+ />
+
+
+
+ NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
+ addItem('Network Obfuscated', {
+ id: `event-${Math.random()}`,
+ fields: [
+ { key: 'Date', value: new Date().toLocaleString() },
+ { key: 'Url', value: networkData.url },
+ { key: 'Method', value: networkData.method },
+ { key: 'Request Body', value: networkData.requestBody },
+ { key: 'Response', value: networkData.responseBody?.toString() ?? '' },
+ { key: 'Request Headers', value: networkData.requestHeaders.toString() },
+ { key: 'Response Headers', value: networkData.requestHeaders.toString() },
+ ],
+ });
+ return networkData;
+ })
+ }
+ />
+
+
+ NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
+ addItem('Network Obfuscated', {
+ id: `event-${Math.random()}`,
+ fields: [
+ { key: 'Date', value: new Date().toLocaleString() },
+ { key: 'Url', value: networkData.url },
+ { key: 'Method', value: networkData.method },
+ { key: 'Request Body', value: networkData.requestBody },
+ { key: 'Response', value: networkData.responseBody?.toString() ?? '' },
+ { key: 'Request Headers', value: networkData.requestHeaders.toString() },
+ { key: 'Response Headers', value: networkData.requestHeaders.toString() },
+ ],
+ });
+ throw Error(' Error from Network Obfuscated');
+ })
+ }
+ />
+
);
diff --git a/examples/default/src/screens/apm/network/NetworkStateScreen.tsx b/examples/default/src/screens/apm/network/NetworkStateScreen.tsx
new file mode 100644
index 000000000..b58954b2d
--- /dev/null
+++ b/examples/default/src/screens/apm/network/NetworkStateScreen.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../../navigation/HomeStack';
+import { ListTile } from '../../../components/ListTile';
+import { Screen } from '../../../components/Screen';
+
+export enum NetworkState {
+ Enabled = 'Enabled',
+ Disabled = 'Disabled',
+}
+
+export interface NetworkStateScreenProp {
+ state: NetworkState;
+ setState: (state: NetworkState) => void;
+}
+
+export const NetworkStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { state, setState } = route.params;
+ return (
+
+ setState(NetworkState.Enabled)}
+ subtitle={state === NetworkState.Enabled ? 'Selected' : undefined}
+ />
+ setState(NetworkState.Disabled)}
+ subtitle={state === NetworkState.Disabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/BugReportingScreen.tsx b/examples/default/src/screens/bug-reporting/BugReportingScreen.tsx
new file mode 100644
index 000000000..90c7410c6
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/BugReportingScreen.tsx
@@ -0,0 +1,348 @@
+import React, { useState } from 'react';
+
+import Instabug, {
+ BugReporting,
+ InvocationOption,
+ ReportType,
+ ExtendedBugReportMode,
+ WelcomeMessageMode,
+ InvocationEvent,
+ Replies,
+} from 'instabug-reactnative';
+
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import { useToast, ScrollView, Divider } from 'native-base';
+import { Section } from '../../components/Section';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+import { BugReportingState } from './BugReportingStateScreen';
+import { ExtendedBugReportState } from './ExtendedBugReportStateScreen';
+import { useCallbackHandlers } from '../../contexts/callbackContext';
+
+export const BugReportingScreen: React.FC<
+ NativeStackScreenProps
+> = ({ navigation }) => {
+ const toast = useToast();
+ const [reportTypes, setReportTypes] = useState([
+ ReportType.bug,
+ ReportType.feedback,
+ ReportType.question,
+ ]);
+ const [invocationEvents, setInvocationEvents] = useState(['floatingButton']);
+ const [invocationOptions, setInvocationOptions] = useState([]);
+ const [isBugReportingEnabled, setIsBugReportingEnabled] = useState(true);
+ const [extendedBugReportState, setExtendedBugReportState] = useState(
+ ExtendedBugReportState.Disabled,
+ );
+ const [disclaimerText, setDisclaimerText] = useState('');
+ const [isSessionProfilerEnabled, setIsSessionProfilerEnabled] = useState(true);
+ const [isViewHierarchyEnabled, setIsViewHierarchyEnabled] = useState(false);
+ const [isRepliesEnabled, setIsRepliesEnabled] = useState(true);
+ const { addItem } = useCallbackHandlers();
+
+ return (
+
+
+ {
+ navigation.navigate('BugReportingState', {
+ state: isBugReportingEnabled ? BugReportingState.Enabled : BugReportingState.Disabled,
+ setState: (newState: BugReportingState) => {
+ const isEnabled = newState === BugReportingState.Enabled;
+ setIsBugReportingEnabled(isEnabled);
+ BugReporting.setEnabled(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_br_state"
+ />
+
+ {
+ navigation.navigate('ExtendedBugReportState', {
+ state: extendedBugReportState,
+ setState: (newState: ExtendedBugReportState) => {
+ setExtendedBugReportState(newState);
+ switch (newState) {
+ case ExtendedBugReportState.Disabled:
+ BugReporting.setExtendedBugReportMode(ExtendedBugReportMode.disabled);
+ break;
+ case ExtendedBugReportState.EnabledWithRequiredFields:
+ BugReporting.setExtendedBugReportMode(
+ ExtendedBugReportMode.enabledWithRequiredFields,
+ );
+ break;
+ case ExtendedBugReportState.EnabledWithOptionalFields:
+ BugReporting.setExtendedBugReportMode(
+ ExtendedBugReportMode.enabledWithOptionalFields,
+ );
+ break;
+ }
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_extended_br_state"
+ />
+
+ 0 ? reportTypes.join(', ') : 'None selected'}
+ onPress={() => {
+ navigation.navigate('BugReportingTypes', {
+ selectedTypes: reportTypes,
+ setSelectedTypes: (types: ReportType[]) => {
+ setReportTypes(types);
+ BugReporting.setReportTypes(types);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_br_types"
+ />
+
+ {
+ navigation.navigate('DisclaimerText', {
+ initialText: disclaimerText,
+ setText: (text: string) => {
+ setDisclaimerText(text);
+ },
+ });
+ }}
+ testID="id_disclaimer_text"
+ />
+
+ 0 ? invocationEvents.join(', ') : 'None selected'}
+ onPress={() => {
+ navigation.navigate('InvocationEvents', {
+ selectedEvents: invocationEvents,
+ setSelectedEvents: (events: string[]) => {
+ setInvocationEvents(events);
+ const selectedEnums: InvocationEvent[] = events.map((val) => {
+ switch (val) {
+ case 'floatingButton':
+ return InvocationEvent.floatingButton;
+ case 'twoFingersSwipe':
+ return InvocationEvent.twoFingersSwipe;
+ case 'screenshot':
+ return InvocationEvent.screenshot;
+ case 'shake':
+ return InvocationEvent.shake;
+ default:
+ throw new Error('Invalid invocation event selected');
+ }
+ });
+ BugReporting.setInvocationEvents(selectedEnums);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_invocation_events"
+ />
+
+ 0 ? invocationOptions.join(', ') : 'None selected'}
+ onPress={() => {
+ navigation.navigate('InvocationOptions', {
+ selectedOptions: invocationOptions,
+ setSelectedOptions: (options: string[]) => {
+ setInvocationOptions(options);
+ const selectedEnums: InvocationOption[] = options.map((val) => {
+ switch (val) {
+ case 'commentFieldRequired':
+ return InvocationOption.commentFieldRequired;
+ case 'emailFieldHidden':
+ return InvocationOption.emailFieldHidden;
+ case 'emailFieldOptional':
+ return InvocationOption.emailFieldOptional;
+ case 'disablePostSendingDialog':
+ return InvocationOption.disablePostSendingDialog;
+ default:
+ throw new Error('Invalid invocation option selected');
+ }
+ });
+ BugReporting.setOptions(selectedEnums);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_invocation_options"
+ />
+
+ {
+ navigation.navigate('SessionProfiler', {
+ isEnabled: isSessionProfilerEnabled,
+ setIsEnabled: (enabled: boolean) => {
+ setIsSessionProfilerEnabled(enabled);
+ Instabug.setSessionProfilerEnabled(enabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_session_profiler"
+ />
+
+ {
+ navigation.navigate('ViewHierarchy', {
+ isEnabled: isViewHierarchyEnabled,
+ setIsEnabled: (enabled: boolean) => {
+ setIsViewHierarchyEnabled(enabled);
+ BugReporting.setViewHierarchyEnabled(enabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_view_hierarchy"
+ />
+
+ {
+ navigation.navigate('RepliesState', {
+ isEnabled: isRepliesEnabled,
+ setIsEnabled: (enabled: boolean) => {
+ setIsRepliesEnabled(enabled);
+ Replies.setEnabled(enabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_replies"
+ />
+
+ navigation.navigate('UserConsent')}
+ testID="id_user_consent"
+ />
+
+
+
+ Instabug.show()} testID="id_show_button" />
+ BugReporting.show(ReportType.bug, [])}
+ testID="id_send_bug_report"
+ />
+
+ BugReporting.show(ReportType.feedback, [InvocationOption.emailFieldHidden])
+ }
+ testID="id_send_feedback"
+ />
+ BugReporting.show(ReportType.question, [])}
+ testID="id_send_question"
+ />
+
+ Instabug.showWelcomeMessage(WelcomeMessageMode.beta)}
+ />
+ Instabug.showWelcomeMessage(WelcomeMessageMode.live)}
+ />
+
+
+
+
+
+ BugReporting.onInvokeHandler(function () {
+ toast.show({
+ description: 'Invoke Callback Handler',
+ });
+
+ addItem('Invoke Handler', {
+ id: `event-${Math.random()}`,
+ fields: [{ key: 'Date', value: new Date().toLocaleString() }],
+ });
+ })
+ }
+ />
+
+ BugReporting.onInvokeHandler(function () {})}
+ />
+
+
+ BugReporting.onInvokeHandler(function () {
+ addItem('Invoke Handler', {
+ id: `event-${Math.random()}`,
+ fields: [{ key: 'Date', value: new Date().toLocaleString() }],
+ });
+ throw new Error('💥 Crash inside onInvokeHandler');
+ })
+ }
+ />
+
+ BugReporting.onSDKDismissedHandler(function () {
+ toast.show({
+ description: 'onSDKDismissedHandler Callback Handler',
+ });
+ addItem('onSDKDismissedHandler', {
+ id: `event-${Math.random()}`,
+ fields: [{ key: 'Date', value: new Date().toLocaleString() }],
+ });
+ })
+ }
+ />
+
+ BugReporting.onSDKDismissedHandler(function () {})}
+ />
+
+
+ BugReporting.onSDKDismissedHandler(function () {
+ addItem('onSDKDismissedHandler', {
+ id: `event-${Math.random()}`,
+ fields: [{ key: 'Date', value: new Date().toLocaleString() }],
+ });
+
+ throw new Error('💥 Crash inside onSDKDismissedHandler');
+ })
+ }
+ />
+
+
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/BugReportingStateScreen.tsx b/examples/default/src/screens/bug-reporting/BugReportingStateScreen.tsx
new file mode 100644
index 000000000..1df7acaf7
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/BugReportingStateScreen.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export enum BugReportingState {
+ Enabled = 'Enabled',
+ Disabled = 'Disabled',
+}
+
+export interface BugReportingStateScreenProp {
+ state: BugReportingState;
+ setState: (state: BugReportingState) => void;
+}
+
+export const BugReportingStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { state, setState } = route.params;
+ return (
+
+ setState(BugReportingState.Enabled)}
+ subtitle={state === BugReportingState.Enabled ? 'Selected' : undefined}
+ />
+ setState(BugReportingState.Disabled)}
+ subtitle={state === BugReportingState.Disabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/BugReportingTypesScreen.tsx b/examples/default/src/screens/bug-reporting/BugReportingTypesScreen.tsx
new file mode 100644
index 000000000..bbaa52989
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/BugReportingTypesScreen.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+import { ReportType } from 'instabug-reactnative';
+
+export interface BugReportingTypesScreenProp {
+ selectedTypes: ReportType[];
+ setSelectedTypes: (types: ReportType[]) => void;
+}
+
+export const BugReportingTypesScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { selectedTypes, setSelectedTypes } = route.params;
+
+ const isSelected = (types: ReportType[]) => {
+ return (
+ types.length === selectedTypes.length && types.every((type) => selectedTypes.includes(type))
+ );
+ };
+
+ return (
+
+ setSelectedTypes([ReportType.bug])}
+ subtitle={isSelected([ReportType.bug]) ? 'Selected' : undefined}
+ />
+ setSelectedTypes([ReportType.feedback])}
+ subtitle={isSelected([ReportType.feedback]) ? 'Selected' : undefined}
+ />
+ setSelectedTypes([ReportType.question])}
+ subtitle={isSelected([ReportType.question]) ? 'Selected' : undefined}
+ />
+ setSelectedTypes([ReportType.bug, ReportType.question])}
+ subtitle={isSelected([ReportType.bug, ReportType.question]) ? 'Selected' : undefined}
+ />
+ setSelectedTypes([ReportType.bug, ReportType.feedback, ReportType.question])}
+ subtitle={
+ isSelected([ReportType.bug, ReportType.feedback, ReportType.question])
+ ? 'Selected'
+ : undefined
+ }
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/DisclaimerTextScreen.tsx b/examples/default/src/screens/bug-reporting/DisclaimerTextScreen.tsx
new file mode 100644
index 000000000..967a7a853
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/DisclaimerTextScreen.tsx
@@ -0,0 +1,45 @@
+import React, { useState } from 'react';
+
+import { Screen } from '../../components/Screen';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+import { BugReporting } from 'instabug-reactnative';
+import { Input, Button, VStack } from 'native-base';
+
+export interface DisclaimerTextScreenProp {
+ initialText: string;
+ setText: (text: string) => void;
+}
+
+export const DisclaimerTextScreen: React.FC<
+ NativeStackScreenProps
+> = ({ navigation, route }) => {
+ const { initialText, setText } = route.params;
+ const [localText, setLocalText] = useState(initialText);
+
+ const handleSave = () => {
+ setText(localText);
+ BugReporting.setDisclaimerText(localText);
+ navigation.goBack();
+ };
+
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/ExtendedBugReportStateScreen.tsx b/examples/default/src/screens/bug-reporting/ExtendedBugReportStateScreen.tsx
new file mode 100644
index 000000000..ef6297783
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/ExtendedBugReportStateScreen.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export enum ExtendedBugReportState {
+ Disabled = 'Disabled',
+ EnabledWithRequiredFields = 'EnabledWithRequiredFields',
+ EnabledWithOptionalFields = 'EnabledWithOptionalFields',
+}
+
+export interface ExtendedBugReportStateScreenProp {
+ state: ExtendedBugReportState;
+ setState: (state: ExtendedBugReportState) => void;
+}
+
+export const ExtendedBugReportStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { state, setState } = route.params;
+ return (
+
+ setState(ExtendedBugReportState.Disabled)}
+ subtitle={state === ExtendedBugReportState.Disabled ? 'Selected' : undefined}
+ />
+ setState(ExtendedBugReportState.EnabledWithRequiredFields)}
+ subtitle={
+ state === ExtendedBugReportState.EnabledWithRequiredFields ? 'Selected' : undefined
+ }
+ />
+ setState(ExtendedBugReportState.EnabledWithOptionalFields)}
+ subtitle={
+ state === ExtendedBugReportState.EnabledWithOptionalFields ? 'Selected' : undefined
+ }
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/InvocationEventsScreen.tsx b/examples/default/src/screens/bug-reporting/InvocationEventsScreen.tsx
new file mode 100644
index 000000000..3c334930b
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/InvocationEventsScreen.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface InvocationEventsScreenProp {
+ selectedEvents: string[];
+ setSelectedEvents: (events: string[]) => void;
+}
+
+export const InvocationEventsScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { selectedEvents, setSelectedEvents } = route.params;
+
+ const isSelected = (events: string[]) => {
+ return (
+ events.length === selectedEvents.length &&
+ events.every((event) => selectedEvents.includes(event))
+ );
+ };
+
+ return (
+
+ setSelectedEvents(['floatingButton'])}
+ testID="id_floating_button"
+ subtitle={isSelected(['floatingButton']) ? 'Selected' : undefined}
+ />
+
+ setSelectedEvents(['twoFingersSwipe'])}
+ testID="id_two_fingers_swipe"
+ subtitle={isSelected(['twoFingersSwipe']) ? 'Selected' : undefined}
+ />
+
+ setSelectedEvents(['screenshot'])}
+ testID="id_screenshot"
+ subtitle={isSelected(['screenshot']) ? 'Selected' : undefined}
+ />
+
+ setSelectedEvents(['shake'])}
+ testID="id_shake"
+ subtitle={isSelected(['shake']) ? 'Selected' : undefined}
+ />
+
+ setSelectedEvents(['floatingButton', 'shake', 'screenshot'])}
+ testID="id_common"
+ subtitle={isSelected(['floatingButton', 'shake', 'screenshot']) ? 'Selected' : undefined}
+ />
+
+
+ setSelectedEvents(['floatingButton', 'twoFingersSwipe', 'screenshot', 'shake'])
+ }
+ testID="id_all"
+ subtitle={
+ isSelected(['floatingButton', 'twoFingersSwipe', 'screenshot', 'shake'])
+ ? 'Selected'
+ : undefined
+ }
+ />
+ setSelectedEvents([])}
+ testID="id_none"
+ subtitle={isSelected([]) ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/InvocationOptionsScreen.tsx b/examples/default/src/screens/bug-reporting/InvocationOptionsScreen.tsx
new file mode 100644
index 000000000..0d3ddb80e
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/InvocationOptionsScreen.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface InvocationOptionsScreenProp {
+ selectedOptions: string[];
+ setSelectedOptions: (options: string[]) => void;
+}
+
+export const InvocationOptionsScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { selectedOptions, setSelectedOptions } = route.params;
+
+ const isSelected = (options: string[]) => {
+ return (
+ options.length === selectedOptions.length &&
+ options.every((option) => selectedOptions.includes(option))
+ );
+ };
+
+ return (
+
+ setSelectedOptions(['commentFieldRequired'])}
+ testID="id_comment_field_required"
+ subtitle={isSelected(['commentFieldRequired']) ? 'Selected' : undefined}
+ />
+
+ setSelectedOptions(['emailFieldHidden'])}
+ testID="id_email_field_hidden"
+ subtitle={isSelected(['emailFieldHidden']) ? 'Selected' : undefined}
+ />
+
+ setSelectedOptions(['emailFieldOptional'])}
+ testID="id_email_field_optional"
+ subtitle={isSelected(['emailFieldOptional']) ? 'Selected' : undefined}
+ />
+
+ setSelectedOptions(['disablePostSendingDialog'])}
+ testID="id_disable_post_sending_dialog"
+ subtitle={isSelected(['disablePostSendingDialog']) ? 'Selected' : undefined}
+ />
+
+ setSelectedOptions(['commentFieldRequired', 'emailFieldHidden'])}
+ testID="id_comment_field_required_email_field_hidden"
+ subtitle={isSelected(['commentFieldRequired', 'emailFieldHidden']) ? 'Selected' : undefined}
+ />
+
+ setSelectedOptions([])}
+ testID="id_none"
+ subtitle={isSelected([]) ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/RepliesStateScreen.tsx b/examples/default/src/screens/bug-reporting/RepliesStateScreen.tsx
new file mode 100644
index 000000000..a4f152c30
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/RepliesStateScreen.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface RepliesStateScreenProp {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+}
+
+export const RepliesStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { isEnabled, setIsEnabled } = route.params;
+
+ return (
+
+ setIsEnabled(true)}
+ testID="id_enabled"
+ subtitle={isEnabled ? 'Selected' : undefined}
+ />
+ setIsEnabled(false)}
+ testID="id_disabled"
+ subtitle={!isEnabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/SessionProfilerScreen.tsx b/examples/default/src/screens/bug-reporting/SessionProfilerScreen.tsx
new file mode 100644
index 000000000..63938a49d
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/SessionProfilerScreen.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface SessionProfilerScreenProp {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+}
+
+export const SessionProfilerScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { isEnabled, setIsEnabled } = route.params;
+
+ return (
+
+ setIsEnabled(true)}
+ testID="id_enabled"
+ subtitle={isEnabled ? 'Selected' : undefined}
+ />
+ setIsEnabled(false)}
+ testID="id_disabled"
+ subtitle={!isEnabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/UserConsentScreen.tsx b/examples/default/src/screens/bug-reporting/UserConsentScreen.tsx
new file mode 100644
index 000000000..8845cef94
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/UserConsentScreen.tsx
@@ -0,0 +1,145 @@
+import React, { useState } from 'react';
+import { ScrollView, StyleSheet, View, Text, Alert } from 'react-native';
+import { BugReporting, userConsentActionType } from 'instabug-reactnative';
+import { Screen } from '../../components/Screen';
+import { Section } from '../../components/Section';
+import { Button, VStack } from 'native-base';
+import { InputField } from '../../components/InputField';
+import { Select } from '../../components/Select';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+const styles = StyleSheet.create({
+ inputWrapper: {
+ padding: 4,
+ flex: 1,
+ },
+ inputTitle: {
+ fontSize: 12,
+ fontWeight: 'bold',
+ color: 'black',
+ paddingLeft: 4,
+ paddingBottom: 4,
+ },
+});
+
+export const UserConsentScreen: React.FC<
+ NativeStackScreenProps
+> = ({ navigation }) => {
+ const [key, setKey] = useState('');
+ const [description, setDescription] = useState('');
+ const [mandatory, setMandatory] = useState(false);
+ const [checked, setChecked] = useState(false);
+ const [actionType, setActionType] = useState(undefined);
+
+ const handleSubmit = () => {
+ BugReporting.addUserConsent(key, description, mandatory, checked, actionType);
+ Alert.alert('User Consent Added', 'User consent added successfully');
+ navigation.goBack();
+ };
+
+ return (
+
+
+
+
+
+ Key
+
+
+
+ Description
+
+
+
+ Mandatory
+
+
+ Checked
+
+
+ Action Type
+
+
+
+
+
+
+
+ );
+};
diff --git a/examples/default/src/screens/bug-reporting/ViewHierarchyScreen.tsx b/examples/default/src/screens/bug-reporting/ViewHierarchyScreen.tsx
new file mode 100644
index 000000000..80f565fc7
--- /dev/null
+++ b/examples/default/src/screens/bug-reporting/ViewHierarchyScreen.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface ViewHierarchyScreenProp {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+}
+
+export const ViewHierarchyScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { isEnabled, setIsEnabled } = route.params;
+
+ return (
+
+ setIsEnabled(true)}
+ testID="id_enabled"
+ subtitle={isEnabled ? 'Selected' : undefined}
+ />
+ setIsEnabled(false)}
+ testID="id_disabled"
+ subtitle={!isEnabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/crash-reporting/CrashReportingStateScreen.tsx b/examples/default/src/screens/crash-reporting/CrashReportingStateScreen.tsx
new file mode 100644
index 000000000..9741c2436
--- /dev/null
+++ b/examples/default/src/screens/crash-reporting/CrashReportingStateScreen.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface CrashReportingStateScreenProp {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+}
+
+export const CrashReportingStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { isEnabled, setIsEnabled } = route.params;
+
+ return (
+
+ setIsEnabled(true)}
+ testID="id_enabled"
+ subtitle={isEnabled ? 'Selected' : undefined}
+ />
+ setIsEnabled(false)}
+ testID="id_disabled"
+ subtitle={!isEnabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/crash-reporting/FatalCrashesScreen.tsx b/examples/default/src/screens/crash-reporting/FatalCrashesScreen.tsx
new file mode 100644
index 000000000..867e85fcc
--- /dev/null
+++ b/examples/default/src/screens/crash-reporting/FatalCrashesScreen.tsx
@@ -0,0 +1,152 @@
+import React from 'react';
+import { Alert, Platform, ScrollView, StyleSheet, Text } from 'react-native';
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import { Section } from '../../components/Section';
+import { PlatformListTile } from '../../components/PlatformListTile';
+import { NativeExampleCrashReporting } from '../../native/NativeCrashReporting';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+const styles = StyleSheet.create({
+ sectionBodyText: {
+ color: 'gray',
+ },
+});
+
+export const FatalCrashesScreen: React.FC<
+ NativeStackScreenProps
+> = () => {
+ function throwUnhandledException(error: Error, isPromise: boolean = false) {
+ const appName = 'Instabug Test App';
+ const rejectionType = isPromise ? 'Promise Rejection ' : '';
+ const errorMessage = `Unhandled ${rejectionType}${error.name} from ${appName}`;
+
+ if (!error.message) {
+ console.log(`IBG-CRSH | Error message: ${error.message}`);
+ error.message = errorMessage;
+ }
+
+ if (isPromise) {
+ console.log('IBG-CRSH | Promise');
+ Promise.reject(error).then(() =>
+ Alert.alert(`Promise Rejection Crash report for ${error.name} is Sent!`),
+ );
+ } else {
+ throw error;
+ }
+ }
+
+ return (
+
+
+
+
+ Fatal Crashes can only be tested in release mode
+
+ These buttons will crash the application.
+ throwUnhandledException(Error(), true)}
+ testID="id_reject_unhandled_promise"
+ />
+ throwUnhandledException(Error())}
+ testID="id_throw_unhandled_fatal_exception"
+ />
+ throwUnhandledException(new SyntaxError())}
+ testID="id_throw_unhandled_syntax_exception"
+ />
+ throwUnhandledException(new RangeError())}
+ testID="id_throw_unhandled_range_exception"
+ />
+ throwUnhandledException(new ReferenceError())}
+ testID="id_throw_unhandled_reference_exception"
+ />
+ throwUnhandledException(new URIError())}
+ testID="id_throw_unhandled_uri_error_exception"
+ />
+ NativeExampleCrashReporting.sendNativeFatalCrash()}
+ testID="id_throw_unhandled_native_exception"
+ />
+ NativeExampleCrashReporting.sendFatalHang()}
+ testID="id_send_native_fatal_hang"
+ />
+ NativeExampleCrashReporting.sendANR()}
+ platform={'android'}
+ testID="id_send_native_anr"
+ />
+ NativeExampleCrashReporting.sendOOM()}
+ testID="id_throw_unhandled_native_oom_exception"
+ />
+
+
+ {Platform.OS === 'android' && (
+
+
+ NDK Crashes can only be tested in release mode.
+
+ These buttons will crash the application.
+ {
+ console.log('Sending NDK SIGSEGV Crash');
+ await NativeExampleCrashReporting.causeSIGSEGVCrash();
+ }}
+ testID="id_throw_unhandled_ndk_sigsegv_crash"
+ />
+ {
+ console.log('Sending NDK SIGFPE Crash');
+ await NativeExampleCrashReporting.causeSIGFPECrash();
+ }}
+ testID="id_throw_unhandled_ndk_sigfpe_crash"
+ />
+ {
+ console.log('Sending NDK SIGILL Crash');
+ await NativeExampleCrashReporting.causeSIGILLCrash();
+ }}
+ testID="id_throw_unhandled_ndk_sigill_crash"
+ />
+ {
+ console.log('Sending NDK SIGBUS Crash');
+ await NativeExampleCrashReporting.causeSIGBUSCrash();
+ }}
+ testID="id_throw_unhandled_ndk_sigbus_crash"
+ />
+ {
+ console.log('Sending NDK SIGTRAP Crash');
+ await NativeExampleCrashReporting.causeSIGTRAPCrash();
+ }}
+ testID="id_throw_unhandled_ndk_sigtrap_crash"
+ />
+
+ )}
+
+
+ );
+};
diff --git a/examples/default/src/screens/crash-reporting/NDKCrashesStateScreen.tsx b/examples/default/src/screens/crash-reporting/NDKCrashesStateScreen.tsx
new file mode 100644
index 000000000..669969da0
--- /dev/null
+++ b/examples/default/src/screens/crash-reporting/NDKCrashesStateScreen.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Screen } from '../../components/Screen';
+import { ListTile } from '../../components/ListTile';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+export interface NDKCrashesStateScreenProp {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+}
+
+export const NDKCrashesStateScreen: React.FC<
+ NativeStackScreenProps
+> = ({ route }) => {
+ const { isEnabled, setIsEnabled } = route.params;
+
+ return (
+
+ setIsEnabled(true)}
+ testID="id_enabled"
+ subtitle={isEnabled ? 'Selected' : undefined}
+ />
+ setIsEnabled(false)}
+ testID="id_disabled"
+ subtitle={!isEnabled ? 'Selected' : undefined}
+ />
+
+ );
+};
diff --git a/examples/default/src/screens/crash-reporting/NonFatalCrashesScreen.tsx b/examples/default/src/screens/crash-reporting/NonFatalCrashesScreen.tsx
new file mode 100644
index 000000000..e66c34794
--- /dev/null
+++ b/examples/default/src/screens/crash-reporting/NonFatalCrashesScreen.tsx
@@ -0,0 +1,190 @@
+import React, { useState } from 'react';
+import { Alert, ScrollView, StyleSheet, View } from 'react-native';
+import { CrashReporting, NonFatalErrorLevel } from 'instabug-reactnative';
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import { Section } from '../../components/Section';
+import { NativeExampleCrashReporting } from '../../native/NativeCrashReporting';
+import { Button, Divider, VStack } from 'native-base';
+import { InputField } from '../../components/InputField';
+import { Select } from '../../components/Select';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+
+const styles = StyleSheet.create({
+ inputWrapper: {
+ padding: 4,
+ flex: 1,
+ },
+ formContainer: {
+ flexDirection: 'row',
+ alignItems: 'stretch',
+ },
+});
+
+export const NonFatalCrashesScreen: React.FC<
+ NativeStackScreenProps
+> = () => {
+ const [userAttributeKey, setUserAttributeKey] = useState('');
+ const [userAttributeValue, setUserAttributeValue] = useState('');
+ const [crashNameValue, setCrashNameValue] = useState('');
+ const [crashFingerprint, setCrashFingerprint] = useState('');
+ const [crashLevelValue, setCrashLevelValue] = useState(
+ NonFatalErrorLevel.error,
+ );
+
+ function throwHandledException(error: Error) {
+ try {
+ if (!error.message) {
+ const appName = 'Instabug Test App';
+ error.message = `Handled ${error.name} From ${appName}`;
+ }
+ throw error;
+ } catch (err) {
+ if (err instanceof Error) {
+ CrashReporting.reportError(err, { level: NonFatalErrorLevel.critical })?.then(() =>
+ Alert.alert(`Crash report for ${err.name} is Sent!`),
+ );
+ }
+ }
+ }
+
+ function sendCrash() {
+ try {
+ const error = new Error(crashNameValue);
+ throw error;
+ } catch (err) {
+ if (err instanceof Error) {
+ const userAttributes: Record = {};
+ if (userAttributeKey && userAttributeValue) {
+ userAttributes[userAttributeKey] = userAttributeValue;
+ }
+ const fingerprint = crashFingerprint.length === 0 ? undefined : crashFingerprint;
+
+ CrashReporting.reportError(err, {
+ userAttributes: userAttributes,
+ fingerprint: fingerprint,
+ level: crashLevelValue,
+ })?.then(() => {
+ Alert.alert(`Crash report for ${crashNameValue} is Sent!`);
+ });
+ }
+ }
+ }
+
+ return (
+
+
+
+ throwHandledException(new Error())}
+ testID="id_throw_handled_exception"
+ />
+ throwHandledException(new SyntaxError())}
+ testID="id_throw_handled_syntax_exception"
+ />
+ throwHandledException(new RangeError())}
+ testID="id_throw_handled_range_exception"
+ />
+ throwHandledException(new ReferenceError())}
+ testID="id_throw_handled_reference_exception"
+ />
+ throwHandledException(new URIError())}
+ testID="id_throw_handled_uri_error_exception"
+ />
+ NativeExampleCrashReporting.sendNativeNonFatal()}
+ testID="id_throw_handled_native_exception"
+ />
+
+
+
+
+
+
+ setCrashNameValue(key)}
+ value={crashNameValue}
+ testID="id_crash_name"
+ />
+
+
+
+ setUserAttributeKey(key)}
+ value={userAttributeKey}
+ testID="id_user_attribute_key"
+ />
+
+
+ setUserAttributeValue(value)}
+ value={userAttributeValue}
+ testID="id_user_attribute_value"
+ />
+
+
+
+
+
+
+ setCrashFingerprint(text)}
+ value={crashFingerprint}
+ testID="id_crash_fingerprint"
+ />
+
+
+
+
+
+
+
+ );
+};
diff --git a/examples/default/src/screens/SettingsScreen.tsx b/examples/default/src/screens/settings/SettingsScreen.tsx
similarity index 63%
rename from examples/default/src/screens/SettingsScreen.tsx
rename to examples/default/src/screens/settings/SettingsScreen.tsx
index 764753e70..e8e1a7e60 100644
--- a/examples/default/src/screens/SettingsScreen.tsx
+++ b/examples/default/src/screens/settings/SettingsScreen.tsx
@@ -5,18 +5,25 @@ import Instabug, {
ColorTheme,
InvocationEvent,
Locale,
+ NetworkLogger,
ReproStepsMode,
} from 'instabug-reactnative';
import { InputGroup, InputLeftAddon, useToast, VStack, Button } from 'native-base';
-import { ListTile } from '../components/ListTile';
-import { Screen } from '../components/Screen';
-import { Select } from '../components/Select';
+import { ListTile } from '../../components/ListTile';
+import { Screen } from '../../components/Screen';
+import { Select } from '../../components/Select';
import { StyleSheet, View, ScrollView } from 'react-native';
-import { VerticalListTile } from '../components/VerticalListTile';
-import { InputField } from '../components/InputField';
+import { VerticalListTile } from '../../components/VerticalListTile';
+import { InputField } from '../../components/InputField';
+import { useCallbackHandlers } from '../../contexts/callbackContext';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import type { HomeStackParamList } from '../../navigation/HomeStack';
+import { UserStepsState } from './UserStepsStateScreen';
-export const SettingsScreen: React.FC = () => {
+export const SettingsScreen: React.FC> = ({
+ navigation,
+}) => {
const [color, setColor] = useState('1D82DC');
const [userEmail, setUserEmail] = useState('');
const [userName, setUserName] = useState('');
@@ -25,10 +32,12 @@ export const SettingsScreen: React.FC = () => {
const [userAttributeValue, setUserAttributeValue] = useState('');
const [featureFlagName, setFeatureFlagName] = useState('');
const [featureFlagVariant, setfeatureFlagVariant] = useState('');
+ const [isUserStepEnabled, setIsUserStepEnabled] = useState(true);
const toast = useToast();
const [userAttributesFormError, setUserAttributesFormError] = useState({});
const [featureFlagFormError, setFeatureFlagFormError] = useState({});
+ const { addItem } = useCallbackHandlers();
const validateUserAttributeForm = () => {
const errors: any = {};
@@ -46,6 +55,7 @@ export const SettingsScreen: React.FC = () => {
if (featureFlagName.length === 0) {
errors.featureFlagName = 'Value is required';
}
+
setFeatureFlagFormError(errors);
return Object.keys(errors).length === 0;
};
@@ -182,7 +192,22 @@ export const SettingsScreen: React.FC = () => {
/>
-
+ {
+ navigation.navigate('UserStepsState', {
+ state: isUserStepEnabled ? UserStepsState.Enabled : UserStepsState.Disabled,
+ setState: (newState: UserStepsState) => {
+ const isEnabled = newState === UserStepsState.Enabled;
+ setIsUserStepEnabled(isEnabled);
+ Instabug.setTrackUserSteps(isEnabled);
+ navigation.goBack();
+ },
+ });
+ }}
+ testID="id_user_steps_state"
+ />