Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ examples/.expo/

# OS
.DS_Store

# CocoaPods (library — do not commit generated files)
ios/Podfile.lock
ios/*.xcworkspace/
25 changes: 21 additions & 4 deletions examples/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { UserbackProvider, UserbackSDK } from '@userback/react-native-sdk';
Expand All @@ -10,24 +10,41 @@ import ObserversScreen from './screens/ObserversScreen';

export type Screen = 'home' | 'basic' | 'auth' | 'advanced' | 'observers';

const SCREEN_NAMES: Record<Screen, string> = {
home: 'HomeScreen',
basic: 'BasicScreen',
auth: 'AuthFlowScreen',
advanced: 'AdvancedScreen',
observers: 'ObserversScreen',
};

export default function App() {
const [screen, setScreen] = useState<Screen>('home');
const prevScreen = useRef<Screen | null>(null);

useEffect(() => {
// Start once at the app root — all screens share this instance
UserbackSDK.start({
accessToken: 'YOUR_ACCESS_TOKEN',
userData: {
id: 'user-123',
id: "user-123", // example data
info: {
name: 'Jane Doe',
email: 'jane@example.com',
},
},
}
}
});
return () => UserbackSDK.stop();
}, []);

useEffect(() => {
if (prevScreen.current) {
UserbackSDK.leaveScreen(SCREEN_NAMES[prevScreen.current]);
}
UserbackSDK.enterScreen(SCREEN_NAMES[screen]);
prevScreen.current = screen;
}, [screen]);

return (
<UserbackProvider>
<SafeAreaView style={styles.root}>
Expand Down
44 changes: 39 additions & 5 deletions examples/screens/AdvancedScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { UserbackSDK } from '@userback/react-native-sdk';

Expand All @@ -9,6 +9,11 @@ interface Props {
export default function AdvancedScreen({ goBack }: Props) {
const [replayActive, setReplayActive] = useState(false);

useEffect(() => {
UserbackSDK.enterScreen('AdvancedScreen');
return () => { UserbackSDK.leaveScreen('AdvancedScreen'); };
}, []);

function toggleReplay() {
if (replayActive) {
UserbackSDK.stopSessionReplay();
Expand All @@ -25,20 +30,33 @@ export default function AdvancedScreen({ goBack }: Props) {

<Text style={styles.sectionLabel}>Feedback Modes</Text>
<View style={styles.row}>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('bug')}>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('', 'bug')}>
<Text style={styles.chipText}>Bug</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('feature_request')}>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('', 'feature_request')}>
<Text style={styles.chipText}>Feature</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('general')}>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('', 'general')}>
<Text style={styles.chipText}>General</Text>
</TouchableOpacity>
</View>

<Text style={styles.sectionLabel}>Multiple Projects</Text>
<View style={styles.row}>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('YOUR_PROJECT_KEY_1')}>
<Text style={styles.chipText}>Project 1</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('YOUR_PROJECT_KEY_2')}>
<Text style={styles.chipText}>Project 2</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.chip} onPress={() => UserbackSDK.openForm('YOUR_PROJECT_KEY_3')}>
<Text style={styles.chipText}>Project 3</Text>
</TouchableOpacity>
</View>

<Text style={styles.sectionLabel}>Attach Metadata</Text>
<TouchableOpacity
style={styles.button}
style={[styles.button, styles.inactive]}
onPress={() =>
UserbackSDK.setData({
screen: 'AdvancedScreen',
Expand All @@ -50,6 +68,22 @@ export default function AdvancedScreen({ goBack }: Props) {
<Text style={styles.buttonText}>setData (screen + version)</Text>
</TouchableOpacity>

<Text style={styles.sectionLabel}>Custom Events</Text>
<View style={styles.row}>
<TouchableOpacity
style={styles.chip}
onPress={() => UserbackSDK.addCustomEvent('button_clicked', { screen: 'AdvancedScreen' })}
>
<Text style={styles.chipText}>button_clicked</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.chip}
onPress={() => UserbackSDK.addCustomEvent('purchase_completed', { amount: 99, currency: 'USD' })}
>
<Text style={styles.chipText}>purchase_completed</Text>
</TouchableOpacity>
</View>

<Text style={styles.sectionLabel}>Session Replay</Text>
<TouchableOpacity
style={[styles.button, replayActive ? styles.active : styles.inactive]}
Expand Down
14 changes: 12 additions & 2 deletions examples/screens/BasicScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { UserbackSDK } from '@userback/react-native-sdk';

Expand All @@ -7,17 +7,26 @@ interface Props {
}

export default function BasicScreen({ goBack }: Props) {
useEffect(() => {
UserbackSDK.enterScreen('BasicScreen');
return () => { UserbackSDK.leaveScreen('BasicScreen'); };
}, []);

return (
<View style={styles.container}>
<Text style={styles.title}>Basic Usage</Text>
<Text style={styles.description}>
UserbackProvider and start() live in App.tsx. Any screen can call openForm().
</Text>

<TouchableOpacity style={styles.button} onPress={() => UserbackSDK.openForm()}>
<TouchableOpacity style={styles.button} onPress={() => UserbackSDK.openForm('', 'general')}>
<Text style={styles.buttonText}>Open Feedback</Text>
</TouchableOpacity>

<TouchableOpacity style={[styles.button, styles.surveyButton]} onPress={() => UserbackSDK.openSurvey('YOUR_SURVEY_KEY')}>
<Text style={styles.buttonText}>Open Survey</Text>
</TouchableOpacity>

<TouchableOpacity style={[styles.button, styles.secondary]} onPress={goBack}>
<Text style={styles.secondaryText}>← Back</Text>
</TouchableOpacity>
Expand All @@ -37,6 +46,7 @@ const styles = StyleSheet.create({
marginBottom: 12,
},
buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
surveyButton: { backgroundColor: '#43A047' },
secondary: { backgroundColor: 'transparent' },
secondaryText: { color: '#5C6BC0', fontSize: 16 },
});
84 changes: 78 additions & 6 deletions ios/userbackreactnativesdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
5603264EB79201FDD27E5D89 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F551294207C1F9213E21FEF0 /* PrivacyInfo.xcprivacy */; };
96905EF65AED1B983A6B3ABC /* libPods-userbackreactnativesdk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-userbackreactnativesdk.a */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
Expand All @@ -29,6 +30,7 @@
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = userbackreactnativesdk/SplashScreen.storyboard; sourceTree = "<group>"; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F551294207C1F9213E21FEF0 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = userbackreactnativesdk/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-userbackreactnativesdk/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand All @@ -54,6 +56,7 @@
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB71A68108700A75B9A /* main.m */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
F551294207C1F9213E21FEF0 /* PrivacyInfo.xcprivacy */,
);
name = userbackreactnativesdk;
sourceTree = "<group>";
Expand Down Expand Up @@ -139,11 +142,13 @@
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "userbackreactnativesdk" */;
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
992313AB5608F920D9BC956C /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
9CEBF89FC0445DF638994C66 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -193,6 +198,7 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
5603264EB79201FDD27E5D89 /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -244,20 +250,71 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-userbackreactnativesdk/Pods-userbackreactnativesdk-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-userbackreactnativesdk/Pods-userbackreactnativesdk-resources.sh\"\n";
showEnvVarsInLog = 0;
};
992313AB5608F920D9BC956C /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-userbackreactnativesdk/expo-configure-project.sh\"\n";
};
9CEBF89FC0445DF638994C66 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-userbackreactnativesdk/Pods-userbackreactnativesdk-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-userbackreactnativesdk/Pods-userbackreactnativesdk-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down Expand Up @@ -295,6 +352,7 @@
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = org.name.userbackreactnativesdk;
PRODUCT_NAME = userbackreactnativesdk;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
Expand All @@ -319,6 +377,7 @@
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = org.name.userbackreactnativesdk;
PRODUCT_NAME = userbackreactnativesdk;
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -375,10 +434,17 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
};
name = Debug;
};
Expand Down Expand Up @@ -424,9 +490,15 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
};
name = Release;
Expand Down
Loading
Loading