From 3d84021a872c2599455af28709e29152f10ceaa3 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 26 Aug 2020 13:32:48 +0100 Subject: [PATCH 1/5] feat(analytcis): add 'logScreenView'. dep. setCurrentScreen --- docs/analytics/screen-tracking.md | 13 ++++-- .../analytics/__tests__/analytics.test.ts | 23 ++++------ .../UniversalFirebaseAnalyticsModule.java | 14 ------ .../ReactNativeFirebaseAnalyticsModule.java | 17 ------- packages/analytics/e2e/analytics.e2e.js | 18 ++++---- .../ios/RNFBAnalytics/RNFBAnalyticsModule.m | 18 -------- packages/analytics/lib/index.d.ts | 45 ++++++++++--------- packages/analytics/lib/index.js | 31 +++++++------ packages/analytics/lib/structs.js | 5 +++ packages/analytics/type-test.ts | 4 +- 10 files changed, 75 insertions(+), 113 deletions(-) diff --git a/docs/analytics/screen-tracking.md b/docs/analytics/screen-tracking.md index f64ede98d4..fa616834e8 100644 --- a/docs/analytics/screen-tracking.md +++ b/docs/analytics/screen-tracking.md @@ -13,7 +13,7 @@ therefore there is no "one fits all" solution to screen tracking. The [React Navigation](https://reactnavigation.org/) library allows for various navigation techniques such as Stack, Tab, Native or even custom navigation. The `NavigationController` component which the library exposes provides -access to the current navigation state when a screen changes, allowing you to use the [`setCurrentScreen`](/reference/analytics#setCurrentScreen) +access to the current navigation state when a screen changes, allowing you to use the [`logScreenView`](/reference/analytics#logScreenView) method the Analytics library provides: ```jsx @@ -27,7 +27,9 @@ import { NavigationContainer } from '@react-navigation/native'; const currentRouteName = getActiveRouteName(state); if (previousRouteName !== currentRouteName) { - analytics().setCurrentScreen(currentRouteName, currentRouteName); + analytics().logScreenView({ + screen_name: currentRouteName, + }); } ``` @@ -38,7 +40,7 @@ documentation on the React Navigation website. The [`wix/react-native-navigation`](https://github.com/wix/react-native-navigation) provides 100% native platform navigation for React Native apps. To manually track screens, you need to setup a `componentDidAppear` event listener and manually call the -[`setCurrentScreen`](/reference/analytics#setCurrentScreen) method the Analytics library provides: +[`logScreenView`](/reference/analytics#logScreenView) method the Analytics library provides: ```js import analytics from '@react-native-firebase/analytics'; @@ -46,7 +48,10 @@ import { Navigation } from 'react-native-navigation'; Navigation.events().registerComponentDidAppearListener(({ componentName, componentType }) => { if (componentType === 'Component') { - analytics().setCurrentScreen(componentName, componentName); + analytics().logScreenView({ + screen_name: componentName, + screen_class: componentName, + }); } }); ``` diff --git a/packages/analytics/__tests__/analytics.test.ts b/packages/analytics/__tests__/analytics.test.ts index 234374df8c..e991a6c413 100644 --- a/packages/analytics/__tests__/analytics.test.ts +++ b/packages/analytics/__tests__/analytics.test.ts @@ -46,20 +46,6 @@ describe('Analytics', () => { }); }); - it('errors if screenName not a string', () => { - // @ts-ignore test - expect(() => firebase.analytics().setCurrentScreen(666.1337)).toThrowError( - "'screenName' expected a string value", - ); - }); - - it('errors if screenClassOverride not a string', () => { - // @ts-ignore test - expect(() => firebase.analytics().setCurrentScreen('invertase screen', 666.1337)).toThrowError( - "'screenClassOverride' expected a string value", - ); - }); - it('errors if milliseconds not a number', () => { // @ts-ignore test expect(() => firebase.analytics().setMinimumSessionDuration('123')).toThrowError( @@ -186,6 +172,15 @@ describe('Analytics', () => { ); }); + describe('logScreenView()', () => { + it('errors if param is not an object', () => { + // @ts-ignore test + expect(() => firebase.analytics().logScreenView(123)).toThrowError( + 'firebase.analytics().logScreenView(*):', + ); + }); + }); + describe('logAddPaymentInfo()', () => { it('errors if param is not an object', () => { // @ts-ignore test diff --git a/packages/analytics/android/src/main/java/io/invertase/firebase/analytics/UniversalFirebaseAnalyticsModule.java b/packages/analytics/android/src/main/java/io/invertase/firebase/analytics/UniversalFirebaseAnalyticsModule.java index 70f597cef9..6bd845c0db 100644 --- a/packages/analytics/android/src/main/java/io/invertase/firebase/analytics/UniversalFirebaseAnalyticsModule.java +++ b/packages/analytics/android/src/main/java/io/invertase/firebase/analytics/UniversalFirebaseAnalyticsModule.java @@ -52,20 +52,6 @@ Task setAnalyticsCollectionEnabled(Boolean enabled) { }); } - Task setAnalyticsCollectionEnabled( - Activity currentActivity, - String screenName, - @Nullable String screenClassOverride - ) { - return Tasks.call(() -> { - if (currentActivity == null) return null; - FirebaseAnalytics - .getInstance(getContext()) - .setCurrentScreen(currentActivity, screenName, screenClassOverride); - return null; - }); - } - Task setMinimumSessionDuration(long milliseconds) { return Tasks.call(() -> { FirebaseAnalytics.getInstance(getContext()).setMinimumSessionDuration(milliseconds); diff --git a/packages/analytics/android/src/reactnative/java/io/invertase/firebase/analytics/ReactNativeFirebaseAnalyticsModule.java b/packages/analytics/android/src/reactnative/java/io/invertase/firebase/analytics/ReactNativeFirebaseAnalyticsModule.java index cb3b92b6da..f3ec094746 100644 --- a/packages/analytics/android/src/reactnative/java/io/invertase/firebase/analytics/ReactNativeFirebaseAnalyticsModule.java +++ b/packages/analytics/android/src/reactnative/java/io/invertase/firebase/analytics/ReactNativeFirebaseAnalyticsModule.java @@ -59,23 +59,6 @@ public void setAnalyticsCollectionEnabled(Boolean enabled, Promise promise) { }); } - @ReactMethod - public void setCurrentScreen( - String screenName, - @Nullable String screenClassOverride, - Promise promise - ) { - module - .setAnalyticsCollectionEnabled(getCurrentActivity(), screenName, screenClassOverride) - .addOnCompleteListener(task -> { - if (task.isSuccessful()) { - promise.resolve(task.getResult()); - } else { - rejectPromiseWithExceptionMap(promise, task.getException()); - } - }); - } - @ReactMethod public void setMinimumSessionDuration(double milliseconds, Promise promise) { module.setMinimumSessionDuration((long) milliseconds).addOnCompleteListener(task -> { diff --git a/packages/analytics/e2e/analytics.e2e.js b/packages/analytics/e2e/analytics.e2e.js index 67db9e84f6..a22816bb32 100644 --- a/packages/analytics/e2e/analytics.e2e.js +++ b/packages/analytics/e2e/analytics.e2e.js @@ -56,16 +56,6 @@ describe('analytics()', () => { }); }); - describe('setCurrentScreen()', () => { - it('screenName only', async () => { - await firebase.analytics().setCurrentScreen('invertase screen'); - }); - - it('screenName with screenClassOverride', async () => { - await firebase.analytics().setCurrentScreen('invertase screen', 'invertase class override'); - }); - }); - describe('setMinimumSessionDuration()', () => { it('default duration', async () => { await firebase.analytics().setMinimumSessionDuration(); @@ -116,6 +106,14 @@ describe('analytics()', () => { }); }); + describe('logScreenView()', () => { + it('calls logScreenView', async () => { + await firebase + .analytics() + .logScreenView({ screen_name: 'invertase screen', screen_class: 'invertase class' }); + }); + }); + describe('logAddPaymentInfo()', () => { it('calls logAddPaymentInfo', async () => { await firebase.analytics().logAddPaymentInfo({ diff --git a/packages/analytics/ios/RNFBAnalytics/RNFBAnalyticsModule.m b/packages/analytics/ios/RNFBAnalytics/RNFBAnalyticsModule.m index 88225c634c..0d42fedf3d 100644 --- a/packages/analytics/ios/RNFBAnalytics/RNFBAnalyticsModule.m +++ b/packages/analytics/ios/RNFBAnalytics/RNFBAnalyticsModule.m @@ -67,24 +67,6 @@ - (dispatch_queue_t)methodQueue { return resolve([NSNull null]); } - RCT_EXPORT_METHOD(setCurrentScreen: - (NSString *) screenName - screenClass: - (NSString *) screenClassOverview - resolver: - (RCTPromiseResolveBlock) resolve - rejecter: - (RCTPromiseRejectBlock) reject) { - RCTUnsafeExecuteOnMainQueueSync(^{ - @try { - [FIRAnalytics setScreenName:screenName screenClass:screenClassOverview]; - } @catch (NSException *exception) { - return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception]; - } - return resolve([NSNull null]); - }); - } - RCT_EXPORT_METHOD(setUserId: (NSString *) id resolver: diff --git a/packages/analytics/lib/index.d.ts b/packages/analytics/lib/index.d.ts index 1998923f2d..43f812a47b 100644 --- a/packages/analytics/lib/index.d.ts +++ b/packages/analytics/lib/index.d.ts @@ -324,6 +324,17 @@ export namespace FirebaseAnalyticsTypes { transaction_id?: string; } + export interface ScreenViewParameters { + /** + * Screen name the user is currently viewing. + */ + screen_name?: string; + /** + * Current class associated with the view the user is currently viewing. + */ + screen_class?: string; + } + export interface RefundEventParameters { /** * A product affiliation to designate a supplying company or brick and mortar store location @@ -651,26 +662,6 @@ export namespace FirebaseAnalyticsTypes { * @param enabled A boolean value representing whether Analytics collection is enabled or disabled. Analytics collection is enabled by default. */ setAnalyticsCollectionEnabled(enabled: boolean): Promise; - - /** - * Sets the current screen name. - * - * #### Example - * - * ```js - * await firebase.analytics().setCurrentScreen('ProductScreen', 'ProductScreen'); - * ``` - * - * > Whilst screenClassOverride is optional, it is recommended it is - * always sent as your current class name. For example on Android it will always - * show as 'MainActivity' if you do not specify it. - * - * @param screenName A screen name, e.g. Product. - * @param screenClassOverride On Android, React Native runs in a single activity called - * 'MainActivity'. Setting this parameter overrides the default name shown on logs. - */ - setCurrentScreen(screenName: string, screenClassOverride?: string): Promise; - /** * Sets the minimum engagement time required before starting a session. * @@ -783,6 +774,20 @@ export namespace FirebaseAnalyticsTypes { * ``` */ logPurchase(params: PurchaseEventParameters): Promise; + /** + * Sets the current screen name the user is currently viewing & the class associated with the view. + * + * #### Example + * + * ```js + * await firebase.analytics().logScreenView({ + * screen_class: 'ProductScreen', + * screen_name: 'ProductScreen', + * }); + * ``` + * + */ + logScreenView(params: ScreenViewParameters): Promise; /** * Add Payment Info event. This event signifies that a user has submitted their payment information to your app. * diff --git a/packages/analytics/lib/index.js b/packages/analytics/lib/index.js index f81c798a12..630b4b41ea 100644 --- a/packages/analytics/lib/index.js +++ b/packages/analytics/lib/index.js @@ -102,20 +102,10 @@ class FirebaseAnalyticsModule extends FirebaseModule { return this.native.setAnalyticsCollectionEnabled(enabled); } - setCurrentScreen(screenName, screenClassOverride) { - if (!isString(screenName)) { - throw new Error( - "firebase.analytics().setCurrentScreen(*) 'screenName' expected a string value.", - ); - } - - if (!isUndefined(screenClassOverride) && !isString(screenClassOverride)) { - throw new Error( - "firebase.analytics().setCurrentScreen(_, *) 'screenClassOverride' expected a string value.", - ); - } - - return this.native.setCurrentScreen(screenName, screenClassOverride); + setCurrentScreen() { + console.warn( + 'firebase.analytics().setCurrentScreen(), is now deprecated. Please use firebase.analytics().logScreenView() instead', + ); } setMinimumSessionDuration(milliseconds = 10000) { @@ -214,6 +204,19 @@ class FirebaseAnalyticsModule extends FirebaseModule { ); } + logScreenView(object) { + if (!isObject(object)) { + throw new Error( + 'firebase.analytics().logScreenView(*): The supplied arg must be an object of key/values.', + ); + } + + return this.logEvent( + 'screen_view', + validateStruct(object, structs.ScreenView, 'firebase.analytics().logScreenView(*):'), + ); + } + logAddShippingInfo(object = {}) { if (!isObject(object)) { throw new Error( diff --git a/packages/analytics/lib/structs.js b/packages/analytics/lib/structs.js index eee5c41be4..87f7d54603 100644 --- a/packages/analytics/lib/structs.js +++ b/packages/analytics/lib/structs.js @@ -30,6 +30,11 @@ const Item = struct({ item_variant: 'string?', }); +export const ScreenView = struct({ + screen_class: 'string?', + screen_name: 'string?', +}); + export const AddPaymentInfo = struct({ items: struct.optional([Item]), value: 'number?', diff --git a/packages/analytics/type-test.ts b/packages/analytics/type-test.ts index f4fe2c9d38..7f37af3ed8 100644 --- a/packages/analytics/type-test.ts +++ b/packages/analytics/type-test.ts @@ -65,7 +65,7 @@ console.log(firebase.analytics().resetAnalyticsData); console.log(firebase.analytics().logViewCart); console.log(firebase.analytics().setAnalyticsCollectionEnabled); console.log(firebase.analytics().logSelectPromotion); -console.log(firebase.analytics().setCurrentScreen); +console.log(firebase.analytics().logScreenView); console.log(firebase.analytics().logViewPromotion); console.log(firebase.analytics().setMinimumSessionDuration); console.log(firebase.analytics().setSessionTimeoutDuration); @@ -108,7 +108,7 @@ console.log(analytics().resetAnalyticsData); console.log(analytics().logViewCart); console.log(analytics().setAnalyticsCollectionEnabled); console.log(analytics().logSelectPromotion); -console.log(analytics().setCurrentScreen); +console.log(analytics().logScreenView); console.log(analytics().logViewPromotion); console.log(analytics().setMinimumSessionDuration); console.log(analytics().setSessionTimeoutDuration); From 1c590e82092b926a57417cb870ffc287d1faebf6 Mon Sep 17 00:00:00 2001 From: Russell Wheatley Date: Thu, 27 Aug 2020 10:12:50 +0100 Subject: [PATCH 2/5] Update packages/analytics/lib/index.d.ts Co-authored-by: Mike Hardy --- packages/analytics/lib/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/analytics/lib/index.d.ts b/packages/analytics/lib/index.d.ts index 43f812a47b..79f1aa4cef 100644 --- a/packages/analytics/lib/index.d.ts +++ b/packages/analytics/lib/index.d.ts @@ -775,7 +775,7 @@ export namespace FirebaseAnalyticsTypes { */ logPurchase(params: PurchaseEventParameters): Promise; /** - * Sets the current screen name the user is currently viewing & the class associated with the view. + * Sets or clears the screen name and class the user is currently viewing * * #### Example * From acf5ffd3fea7b855873856dc8082f21d51a12fbc Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Thu, 27 Aug 2020 10:25:20 +0100 Subject: [PATCH 3/5] chore(analytics): feedback --- docs/analytics/screen-tracking.md | 8 ++++---- packages/analytics/lib/index.d.ts | 20 ++++++++++++++++++++ packages/analytics/lib/index.js | 6 +++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/analytics/screen-tracking.md b/docs/analytics/screen-tracking.md index fa616834e8..4a7df8c302 100644 --- a/docs/analytics/screen-tracking.md +++ b/docs/analytics/screen-tracking.md @@ -22,12 +22,12 @@ import { NavigationContainer } from '@react-navigation/native'; { + onStateChange={async (state) => { const previousRouteName = routeNameRef.current; const currentRouteName = getActiveRouteName(state); if (previousRouteName !== currentRouteName) { - analytics().logScreenView({ + await analytics().logScreenView({ screen_name: currentRouteName, }); } @@ -46,9 +46,9 @@ for React Native apps. To manually track screens, you need to setup a `component import analytics from '@react-native-firebase/analytics'; import { Navigation } from 'react-native-navigation'; -Navigation.events().registerComponentDidAppearListener(({ componentName, componentType }) => { +Navigation.events().registerComponentDidAppearListener(async ({ componentName, componentType }) => { if (componentType === 'Component') { - analytics().logScreenView({ + await analytics().logScreenView({ screen_name: componentName, screen_class: componentName, }); diff --git a/packages/analytics/lib/index.d.ts b/packages/analytics/lib/index.d.ts index 79f1aa4cef..b151295c08 100644 --- a/packages/analytics/lib/index.d.ts +++ b/packages/analytics/lib/index.d.ts @@ -662,6 +662,26 @@ export namespace FirebaseAnalyticsTypes { * @param enabled A boolean value representing whether Analytics collection is enabled or disabled. Analytics collection is enabled by default. */ setAnalyticsCollectionEnabled(enabled: boolean): Promise; + + /** + * Sets the current screen name. + * + * #### Example + * + * ```js + * await firebase.analytics().setCurrentScreen('ProductScreen', 'ProductScreen'); + * ``` + * + * > Whilst screenClassOverride is optional, it is recommended it is + * always sent as your current class name. For example on Android it will always + * show as 'MainActivity' if you do not specify it. + * + * @param screenName A screen name, e.g. Product. + * @param screenClassOverride On Android, React Native runs in a single activity called + * @deprecated + * 'MainActivity'. Setting this parameter overrides the default name shown on logs. + */ + setCurrentScreen(screenName: string, screenClassOverride?: string): Promise; /** * Sets the minimum engagement time required before starting a session. * diff --git a/packages/analytics/lib/index.js b/packages/analytics/lib/index.js index 630b4b41ea..f42732673c 100644 --- a/packages/analytics/lib/index.js +++ b/packages/analytics/lib/index.js @@ -102,10 +102,14 @@ class FirebaseAnalyticsModule extends FirebaseModule { return this.native.setAnalyticsCollectionEnabled(enabled); } - setCurrentScreen() { + setCurrentScreen(screenName, screenClassOverride) { console.warn( 'firebase.analytics().setCurrentScreen(), is now deprecated. Please use firebase.analytics().logScreenView() instead', ); + return this.logScreenView({ + screen_name: screenName, + screen_class: screenClassOverride, + }); } setMinimumSessionDuration(milliseconds = 10000) { From 60176c3765e45cac2ed7b681d5d1f10341489ece Mon Sep 17 00:00:00 2001 From: Russell Wheatley Date: Fri, 28 Aug 2020 16:26:06 +0100 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Mike Diarmid Co-authored-by: Mike Hardy --- docs/analytics/screen-tracking.md | 6 +++--- packages/analytics/lib/index.d.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/analytics/screen-tracking.md b/docs/analytics/screen-tracking.md index 4a7df8c302..5b1681f609 100644 --- a/docs/analytics/screen-tracking.md +++ b/docs/analytics/screen-tracking.md @@ -49,9 +49,9 @@ import { Navigation } from 'react-native-navigation'; Navigation.events().registerComponentDidAppearListener(async ({ componentName, componentType }) => { if (componentType === 'Component') { await analytics().logScreenView({ - screen_name: componentName, - screen_class: componentName, - }); + screen_name: componentName, + screen_class: componentName, + }); } }); ``` diff --git a/packages/analytics/lib/index.d.ts b/packages/analytics/lib/index.d.ts index b151295c08..a0796b23b5 100644 --- a/packages/analytics/lib/index.d.ts +++ b/packages/analytics/lib/index.d.ts @@ -678,8 +678,8 @@ export namespace FirebaseAnalyticsTypes { * * @param screenName A screen name, e.g. Product. * @param screenClassOverride On Android, React Native runs in a single activity called + * 'MainActivity'. Setting this parameter overrides the default name shown on logs. * @deprecated - * 'MainActivity'. Setting this parameter overrides the default name shown on logs. */ setCurrentScreen(screenName: string, screenClassOverride?: string): Promise; /** From fde7651a8799a829d2d5c3f6715e37ff4110fbd2 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 28 Aug 2020 16:29:16 +0100 Subject: [PATCH 5/5] chore(analytics): PR review updates --- packages/analytics/e2e/analytics.e2e.js | 10 ++++++++++ packages/analytics/lib/index.js | 1 + 2 files changed, 11 insertions(+) diff --git a/packages/analytics/e2e/analytics.e2e.js b/packages/analytics/e2e/analytics.e2e.js index a22816bb32..e6a5dee900 100644 --- a/packages/analytics/e2e/analytics.e2e.js +++ b/packages/analytics/e2e/analytics.e2e.js @@ -56,6 +56,16 @@ describe('analytics()', () => { }); }); + describe('setCurrentScreen()', () => { + it('screenName only', async () => { + await firebase.analytics().setCurrentScreen('invertase screen'); + }); + + it('screenName with screenClassOverride', async () => { + await firebase.analytics().setCurrentScreen('invertase screen', 'invertase class override'); + }); + }); + describe('setMinimumSessionDuration()', () => { it('default duration', async () => { await firebase.analytics().setMinimumSessionDuration(); diff --git a/packages/analytics/lib/index.js b/packages/analytics/lib/index.js index f42732673c..25d0e0f028 100644 --- a/packages/analytics/lib/index.js +++ b/packages/analytics/lib/index.js @@ -103,6 +103,7 @@ class FirebaseAnalyticsModule extends FirebaseModule { } setCurrentScreen(screenName, screenClassOverride) { + // eslint-disable-next-line no-console console.warn( 'firebase.analytics().setCurrentScreen(), is now deprecated. Please use firebase.analytics().logScreenView() instead', );