Skip to content

Commit

Permalink
Merge pull request #42046 from LCOleksii/fullstory-integration
Browse files Browse the repository at this point in the history
Fullstory integration Update.
  • Loading branch information
puneetlath authored May 27, 2024
2 parents 672124b + e7c2e65 commit b7d6a68
Show file tree
Hide file tree
Showing 24 changed files with 486 additions and 18 deletions.
11 changes: 10 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
apply plugin: "com.google.firebase.firebase-perf"
apply plugin: "fullstory"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"

/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/

/* Fullstory settings */
fullstory {
org 'o-1WN56P-na1'
enabledVariants 'all'
logcatLevel 'debug'
}

react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
Expand Down Expand Up @@ -162,7 +171,7 @@ android {
signingConfig null
// buildTypes take precedence over productFlavors when it comes to the signing configuration,
// thus we need to manually set the signing config, so that the e2e uses the debug config again.
// In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
// In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
productFlavors.all { flavor ->
// All release builds should be signed with the release config ...
flavor.signingConfig signingConfigs.release
Expand Down
6 changes: 5 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ buildscript {
repositories {
google()
mavenCentral()
maven {url "https://maven.fullstory.com"}
}
dependencies {
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("com.google.gms:google-services:4.3.4")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1")
classpath("com.google.firebase:perf-plugin:1.4.1")
// Fullstory integration
classpath ("com.fullstory:gradle-plugin-local:1.47.0")

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
Expand Down Expand Up @@ -70,7 +74,7 @@ allprojects {
// 'mapbox' is the fixed username for Mapbox's Maven repository.
username = 'mapbox'

// The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
// The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
// Run "npm run setup-mapbox-sdk" to set this property in «USER_HOME»/.gradle/gradle.properties

// Example gradle.properties entry:
Expand Down
11 changes: 11 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ const metro = {
['@babel/plugin-proposal-private-property-in-object', {loose: true}],
// The reanimated babel plugin needs to be last, as stated here: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation
'react-native-reanimated/plugin',

/* Fullstory */
'@fullstory/react-native',
[
'@fullstory/babel-plugin-annotate-react',
{
native: true,
setFSTagName: true,
},
],

// Import alias for native devices
[
'module-resolver',
Expand Down
2 changes: 1 addition & 1 deletion config/webpack/webpack.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment):
{from: 'web/apple-touch-icon.png'},
{from: 'assets/images/expensify-app-icon.svg'},
{from: 'web/manifest.json'},
{from: 'web/gtm.js'},
{from: 'web/thirdPartyScripts.js'},
{from: 'assets/css', to: 'css'},
{from: 'assets/fonts/web', to: 'fonts'},
{from: 'assets/sounds', to: 'sounds'},
Expand Down
4 changes: 4 additions & 0 deletions ios/NewExpensify.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@
"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework",
"${BUILT_PRODUCTS_DIR}/Turf/Turf.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/FullStory/FullStory.framework/FullStory",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents",
Expand All @@ -659,6 +660,7 @@
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FullStory.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework",
Expand All @@ -680,6 +682,7 @@
"${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework",
"${BUILT_PRODUCTS_DIR}/Turf/Turf.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/FullStory/FullStory.framework/FullStory",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents",
Expand All @@ -691,6 +694,7 @@
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FullStory.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework",
Expand Down
2 changes: 2 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,5 @@ end
target 'NotificationServiceExtension' do
pod 'AirshipServiceExtension'
end

pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.48.0-xcframework.tar.gz'
37 changes: 35 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,27 @@ PODS:
- GoogleUtilities/Environment (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- fmt (6.2.1)
- FullStory (1.48.0)
- fullstory_react-native (1.4.2):
- FullStory (~> 1.14)
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-debug
- React-Fabric
- React-graphics
- React-ImageManager
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- glog (0.3.5)
- GoogleAppMeasurement (8.8.0):
- GoogleAppMeasurement/AdIdSupport (= 8.8.0)
Expand Down Expand Up @@ -2077,6 +2098,8 @@ DEPENDENCIES:
- ExpoImageManipulator (from `../node_modules/expo-image-manipulator/ios`)
- ExpoModulesCore (from `../node_modules/expo-modules-core`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- "FullStory (from `{:http=>\"https://ios-releases.fullstory.com/fullstory-1.48.0-xcframework.tar.gz\"}`)"
- "fullstory_react-native (from `../node_modules/@fullstory/react-native`)"
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- libevent (~> 2.1.12)
Expand Down Expand Up @@ -2239,6 +2262,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-modules-core"
FBLazyVector:
:path: "../node_modules/react-native/Libraries/FBLazyVector"
FullStory:
:http: https://ios-releases.fullstory.com/fullstory-1.48.0-xcframework.tar.gz
fullstory_react-native:
:path: "../node_modules/@fullstory/react-native"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
Expand Down Expand Up @@ -2429,6 +2456,10 @@ EXTERNAL SOURCES:
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"

CHECKOUT OPTIONS:
FullStory:
:http: https://ios-releases.fullstory.com/fullstory-1.48.0-xcframework.tar.gz

SPEC CHECKSUMS:
Airship: 5a6d3f8a982398940b0d48423bb9b8736717c123
AirshipFrameworkProxy: 7255f4ed9836dc2920f2f1ea5657ced4cee8a35c
Expand All @@ -2454,6 +2485,8 @@ SPEC CHECKSUMS:
FirebasePerformance: 0c01a7a496657d7cea86d40c0b1725259d164c6c
FirebaseRemoteConfig: 2d6e2cfdb49af79535c8af8a80a4a5009038ec2b
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
FullStory: 097347c823c21c655ca25fd8d5e6355a9326ec54
fullstory_react-native: 6cba8a2c054374a24a44dc4310407d9435459cae
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
GoogleAppMeasurement: 5ba1164e3c844ba84272555e916d0a6d3d977e91
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
Expand Down Expand Up @@ -2573,8 +2606,8 @@ SPEC CHECKSUMS:
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2
VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055
Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312
Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70

PODFILE CHECKSUM: a25a81f2b50270f0c0bd0aff2e2ebe4d0b4ec06d
PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b

COCOAPODS: 1.13.0
2 changes: 2 additions & 0 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import '@shopify/flash-list/jestSetup';
import 'react-native-gesture-handler/jestSetup';
import mockStorage from 'react-native-onyx/dist/storage/__mocks__';
import 'setimmediate';
import mockFSLibrary from './setupMockFullstoryLib';
import setupMockImages from './setupMockImages';

setupMockImages();
mockFSLibrary();

// This mock is required as per setup instructions for react-navigation testing
// https://reactnavigation.org/docs/testing/#mocking-native-modules
Expand Down
24 changes: 24 additions & 0 deletions jest/setupMockFullstoryLib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
type FSPageInterface = {
start: jest.Mock<void, []>;
};

export default function mockFSLibrary() {
jest.mock('@fullstory/react-native', () => {
class Fullstory {
consent = jest.fn();

anonymize = jest.fn();

identify = jest.fn();
}

return {
FSPage(): FSPageInterface {
return {
start: jest.fn(),
};
},
default: Fullstory,
};
});
}
49 changes: 49 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
"@formatjs/intl-locale": "^3.3.0",
"@formatjs/intl-numberformat": "^8.5.0",
"@formatjs/intl-pluralrules": "^5.2.2",
"@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
"@fullstory/react-native": "^1.4.2",
"@gorhom/portal": "^1.0.14",
"@invertase/react-native-apple-authentication": "^2.2.2",
"@kie/act-js": "^2.6.0",
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ const ONYXKEYS = {
/** User's Expensify Wallet */
USER_WALLET: 'userWallet',

/** User's metadata that will be used to segmentation */
USER_METADATA: 'userMetadata',

/** Object containing Onfido SDK Token + applicantID */
WALLET_ONFIDO: 'walletOnfido',

Expand Down Expand Up @@ -610,6 +613,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.USER_LOCATION]: OnyxTypes.UserLocation;
[ONYXKEYS.LOGIN_LIST]: OnyxTypes.LoginList;
[ONYXKEYS.SESSION]: OnyxTypes.Session;
[ONYXKEYS.USER_METADATA]: OnyxTypes.UserMetadata;
[ONYXKEYS.STASHED_SESSION]: OnyxTypes.Session;
[ONYXKEYS.BETAS]: OnyxTypes.Beta[];
[ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf<typeof CONST.PRIORITY_MODE>;
Expand Down
3 changes: 3 additions & 0 deletions src/components/FullstoryNative.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import FS from '@fullstory/react-native';

export default FS;
1 change: 1 addition & 0 deletions src/components/VideoPlayer/BaseVideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ function BaseVideoPlayer({
</>
) : (
<View
fsClass="fs-exclude"
style={styles.flex1}
ref={(el) => {
if (!el) {
Expand Down
52 changes: 52 additions & 0 deletions src/libs/Fullstory/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import FullStory, {FSPage} from '@fullstory/react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {UserMetadata} from '@src/types/onyx';

/**
* Fullstory React-Native lib adapter
* Proxy function calls to React-Native lib
* */
const FS = {
/**
* Sets the identity as anonymous using the FullStory library.
*/
anonymize: () => FullStory.anonymize(),

/**
* Sets the identity consent status using the FullStory library.
*/
consent: (c: boolean) => FullStory.consent(c),

/**
* Initializes the FullStory metadata with the provided metadata information.
*/
consentAndIdentify: (value: OnyxEntry<UserMetadata>) => {
try {
// We only use FullStory in production environment
FullStory.consent(true);
FS.fsIdentify(value);

Check failure on line 27 in src/libs/Fullstory/index.native.ts

View workflow job for this annotation

GitHub Actions / typecheck / typecheck

Argument of type 'OnyxEntry<UserMetadata>' is not assignable to parameter of type 'UserMetadata | null'.
} catch (e) {
// error handler
}
},

/**
* Sets the FullStory user identity based on the provided metadata information.
* If the metadata is null or the email is 'undefined', the user identity is anonymized.
* If the metadata contains an accountID, the user identity is defined with it.
*/
fsIdentify: (metadata: UserMetadata | null) => {
if (!metadata?.accountID) {
// anonymize FullStory user identity metadata
FullStory.anonymize();
} else {
// define FullStory user identity
FullStory.identify(String(metadata.accountID), {
properties: metadata,
});
}
},
};

export default FS;
export {FSPage};
Loading

0 comments on commit b7d6a68

Please sign in to comment.