Skip to content

Commit

Permalink
Ryanwang/add pages api (#67)
Browse files Browse the repository at this point in the history
* add pages API to JS spec

* add pages API to iOS

* add Android pages API

* Add Android pages implementation (#68)

* Add Android pages implementation

* updates on feedback
- update legacy RN native module method declarations
- replace System.out with android.util.Log
- initialize reflected methods once
- getUUID is async
- fix page properties merge
- add TS FSPage types

* cleanup and fix turbomodule

* remove private functions from export

* remove page methods from types

* add error message

* generate UUID with Math.random()

* remove async method

* update method type to synchronous

* update android log

---------

Co-authored-by: Ryan Wang <[email protected]>

* call private ios pages API (#69)

* Add Android pages implementation

* updates on feedback
- update legacy RN native module method declarations
- replace System.out with android.util.Log
- initialize reflected methods once
- getUUID is async
- fix page properties merge
- add TS FSPage types

* cleanup and fix turbomodule

* remove private functions from export

* remove page methods from types

* add error message

* generate UUID with Math.random()

* remove async method

* update method type to synchronous

* implement ios pages API

* formatting

* formatting

* Fix rebase regression

* Edit error message

---------

Co-authored-by: Ryan Wang <[email protected]>

* Improve Pages types and add testing (#76)

Co-authored-by: Ryan Wang <[email protected]>

* use uppercase UUIDs, exclude lib/ from jest

* implement new uuid generator, update tests

---------

Co-authored-by: Ryan Wang <[email protected]>
  • Loading branch information
RyanCommits and rwang-fs authored Sep 13, 2023
1 parent 02dbb64 commit 5424a5f
Show file tree
Hide file tree
Showing 16 changed files with 962 additions and 132 deletions.
25 changes: 15 additions & 10 deletions android/src/legacy/java/com/FullStoryModule.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
package com.fullstory.reactnative;

import androidx.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactContext;

import java.util.Map;
import java.util.HashMap;

import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

public class FullStoryModule extends ReactContextBaseJavaModule {

Expand Down Expand Up @@ -86,4 +76,19 @@ public static void log(double level, String message) {
public static void resetIdleTimer() {
FullStoryModuleImpl.resetIdleTimer();
}

@ReactMethod
public static void startPage(String nonce, String pageName, ReadableMap pageProperties) {
FullStoryModuleImpl.startPage(nonce, pageName, pageProperties);
}

@ReactMethod
public static void updatePage(String uuid, ReadableMap pageProperties) {
FullStoryModuleImpl.updatePage(uuid, pageProperties);
}

@ReactMethod
public static void endPage(String uuid) {
FullStoryModuleImpl.endPage(uuid);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.fullstory.reactnative;

import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;
Expand All @@ -8,12 +10,42 @@
import com.fullstory.FSOnReadyListener;
import com.fullstory.FSSessionData;

import java.util.UUID;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import java.lang.reflect.Method;
public class FullStoryModuleImpl {

public static final String NAME = "FullStory";
private static final String TAG = "FullStoryModuleImpl";
public static final boolean reflectionSuccess;
private static final Method PAGE_VIEW;
private static final Method UPDATE_PAGE_PROPERTIES;
private static final Method END_PAGE;

static {
Method pageView;
Method updatePageProperties;
Method endPage;
try {
pageView = FS.class.getMethod("__pageView", UUID.class, String.class, Map.class);
updatePageProperties = FS.class.getMethod("__updatePageProperties", UUID.class, Map.class);
endPage = FS.class.getMethod("__endPage", UUID.class);
} catch (Throwable t) {
pageView = null;
updatePageProperties = null;
endPage = null;
Log.e(TAG, "Unable to access native FullStory pages API. Pages API will not function correctly. " +
"Make sure that your plugin is at least version 1.41; if the issue persists, please contact FullStory Support.");
}

PAGE_VIEW = pageView;
UPDATE_PAGE_PROPERTIES = updatePageProperties;
END_PAGE = endPage;

reflectionSuccess = PAGE_VIEW != null && UPDATE_PAGE_PROPERTIES != null && END_PAGE != null;
}


public static void anonymize() {
FS.anonymize();
Expand Down Expand Up @@ -137,4 +169,45 @@ private static Map toMap(ReadableMap map) {

return map.toHashMap();
}

public static void startPage(String uuid, String pageName, ReadableMap pageProperties) {
if (!reflectionSuccess) {
return;
}

UUID nonce = UUID.fromString(uuid);
try {
PAGE_VIEW.invoke(null, nonce, pageName, toMap(pageProperties));
} catch (Throwable t) {
// this should never happen
Log.e(TAG, "Unexpected error while calling startPage. Please contact FullStory Support.");
}
}

public static void updatePage(String uuid, ReadableMap pageProperties) {
if (!reflectionSuccess) {
return;
}
UUID nonce = UUID.fromString(uuid);

try {
UPDATE_PAGE_PROPERTIES.invoke(null, nonce, toMap(pageProperties));
} catch (Throwable t) {
// this should never happen
Log.e(TAG, "Unexpected error while calling updatePage. Please contact FullStory Support.");
}
}

public static void endPage(String uuid) {
if (!reflectionSuccess) {
return;
}
UUID nonce = UUID.fromString(uuid);
try {
END_PAGE.invoke(null, nonce);
} catch (Throwable t) {
// this should never happen
Log.e(TAG, "Unexpected error while calling endPage. Please contact FullStory Support.");
}
}
}
26 changes: 15 additions & 11 deletions android/src/turbo/java/com/FullStoryModule.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
package com.fullstory.reactnative;

import androidx.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactContext;

import java.util.Map;
import java.util.HashMap;

import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

public class FullStoryModule extends NativeFullStorySpec {

Expand Down Expand Up @@ -87,4 +76,19 @@ public void log(double level, String message) {
public void resetIdleTimer() {
FullStoryModuleImpl.resetIdleTimer();
}

@Override
public void startPage(String nonce, String pageName, ReadableMap pageProperties) {
FullStoryModuleImpl.startPage(nonce, pageName, pageProperties);
}

@Override
public void updatePage(String uuid, ReadableMap pageProperties) {
FullStoryModuleImpl.updatePage(uuid, pageProperties);
}

@Override
public void endPage(String uuid) {
FullStoryModuleImpl.endPage(uuid);
}
}
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};
6 changes: 6 additions & 0 deletions ios/FullStory.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
@interface FullStory : NSObject <RCTBridgeModule, FSDelegate>
@end

@interface FS(FSPrivate)
+ (void) _pageViewWithNonce:(NSUUID *)nonce name:(NSString *)pageName properties:(NSDictionary<NSString *, id> *)properties;
+ (void) _updatePageWithNonce:(NSUUID *)nonce properties:(NSDictionary<NSString *, id> *)properties;
+ (void) _endPageWithNonce:(NSUUID *)nonce;
@end

#ifdef RCT_NEW_ARCH_ENABLED
@interface FullStory () <NativeFullStorySpec>
@end
Expand Down
42 changes: 42 additions & 0 deletions ios/FullStory.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
#import <React/RCTView.h>
#import <React/RCTViewManager.h>
#import <React/RCTComponentData.h>
#import <React/RCTLog.h>

#import "FSReactSwizzle.h"

@implementation FullStory {
RCTPromiseResolveBlock onReadyPromise;
}

NSString *const PagesAPIError = @"Unable to access native FullStory pages API and call %@. Pages API will not function correctly. Make sure that your plugin is at least version 1.41; if the issue persists, please contact FullStory Support.";

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(anonymize)
Expand Down Expand Up @@ -128,6 +131,45 @@ - (void) getCurrentSessionURL:(RCTPromiseResolveBlock)resolve reject:(RCTPromise
});
}

RCT_EXPORT_METHOD(startPage:(NSString *)nonce pageName:(NSString *)pageName pageProperties:(NSDictionary *)pageProperties)
{
if (![FS respondsToSelector:@selector(_pageViewWithNonce:name:properties:)]) {
RCTLogError(PagesAPIError, @"startPage");
} else {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:nonce];

dispatch_async(dispatch_get_main_queue(), ^{
[FS _pageViewWithNonce:uuid name:pageName properties:pageProperties];
});
}
}

RCT_EXPORT_METHOD(endPage:(NSString *)nonce)
{
if (![FS respondsToSelector:@selector(_endPageWithNonce:)]) {
RCTLogError(PagesAPIError, @"endPage");
} else {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:nonce];

dispatch_async(dispatch_get_main_queue(), ^{
[FS _endPageWithNonce:uuid];
});
}
}

RCT_EXPORT_METHOD(updatePage:(NSString *)nonce pageProperties:(NSDictionary *)pageProperties)
{
if (![FS respondsToSelector:@selector(_updatePageWithNonce:properties:)]) {
RCTLogError(PagesAPIError, @"updatePage");
} else {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:nonce];

dispatch_async(dispatch_get_main_queue(), ^{
[FS _updatePageWithNonce:uuid properties:pageProperties];
});
}
}

- (void) fullstoryDidStartSession:(NSString *)sessionUrl {
if (!onReadyPromise)
return;
Expand Down
12 changes: 12 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js'],
setupFiles: ['<rootDir>/setupTests.js'],
testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$',
transform: {
'^.+\\.(js)$': 'babel-jest',
'\\.(ts|tsx)$': 'ts-jest',
},
transformIgnorePatterns: ['<rootDir>/node_modules/(?!(@react-native|react-native)/).*/'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/plugin', '<rootDir>/lib'],
};
Loading

0 comments on commit 5424a5f

Please sign in to comment.