refiner-react-native
Version:
Official React Native wrapper for the Refiner Mobile SDK
279 lines (255 loc) • 9.34 kB
JavaScript
import { NativeModules, NativeEventEmitter, Platform } from "react-native";
// Try to import TurboModule if available
let RNRefinerTurboModule;
let turboModuleAvailable = false;
try {
const { TurboModuleRegistry, NativeModules } = require("react-native");
console.log("Refiner: Available NativeModules:", Object.keys(NativeModules));
console.log("Refiner: TurboModuleRegistry available:", !!TurboModuleRegistry);
// First try to get the TurboModule
try {
RNRefinerTurboModule = TurboModuleRegistry.getEnforcing("RNRefiner");
turboModuleAvailable = true;
console.log(
"Refiner: TurboModule loaded successfully via TurboModuleRegistry"
);
} catch (turboError) {
console.log(
"Refiner: TurboModule not available via TurboModuleRegistry:",
turboError.message
);
// If TurboModuleRegistry fails, try getting it from NativeModules
if (NativeModules.RNRefiner) {
RNRefinerTurboModule = NativeModules.RNRefiner;
turboModuleAvailable = true;
console.log(
"Refiner: Module loaded successfully via NativeModules.RNRefiner"
);
console.log(
"Refiner: This is likely the legacy module, not the TurboModule"
);
} else {
console.log("Refiner: Module not found in NativeModules either");
}
}
if (RNRefinerTurboModule) {
console.log("Refiner: Module methods:", Object.keys(RNRefinerTurboModule));
}
} catch (e) {
console.log("Refiner: Error loading module:", e.message);
// Module not available, will fall back to legacy bridge
}
// Determine which module to use and set architecture flag
let RNRefinerModule;
let isNewArchitecture = false;
// In New Architecture with bridgeless mode, prioritize legacy module since TurboModules
// from local dependencies may not be properly autolinked
if (NativeModules.RNRefiner) {
RNRefinerModule = NativeModules.RNRefiner;
isNewArchitecture = false;
console.log("Refiner: Using legacy module from NativeModules");
} else if (turboModuleAvailable && RNRefinerTurboModule) {
RNRefinerModule = RNRefinerTurboModule;
isNewArchitecture = true;
console.log("Refiner: Using TurboModule (New Architecture)");
} else {
// If neither TurboModule nor legacy module is available, try to access it through TurboModuleRegistry
console.log(
"Refiner: Neither TurboModule nor legacy module found, attempting fallback"
);
try {
const { TurboModuleRegistry } = require("react-native");
// Try without getEnforcing to avoid exceptions
RNRefinerModule = TurboModuleRegistry.get("RNRefiner");
if (RNRefinerModule) {
isNewArchitecture = true;
console.log("Refiner: Found module via TurboModuleRegistry.get()");
} else {
console.log(
"Refiner: Module not found via TurboModuleRegistry.get() either"
);
RNRefinerModule = null;
}
} catch (e) {
console.log("Refiner: Error accessing TurboModuleRegistry:", e.message);
RNRefinerModule = null;
}
}
// Create a stub module for when native module is not available
const createStubModule = () => {
const stubMethod =
(methodName) =>
(...args) => {
// Stub method - no logging needed
};
return {
initialize: stubMethod("initialize"),
setProject: stubMethod("setProject"),
identifyUser: stubMethod("identifyUser"),
setUser: stubMethod("setUser"),
resetUser: stubMethod("resetUser"),
trackEvent: stubMethod("trackEvent"),
trackScreen: stubMethod("trackScreen"),
ping: stubMethod("ping"),
showForm: stubMethod("showForm"),
dismissForm: stubMethod("dismissForm"),
closeForm: stubMethod("closeForm"),
addToResponse: stubMethod("addToResponse"),
startSession: stubMethod("startSession"),
addListener: stubMethod("addListener"),
removeListeners: stubMethod("removeListeners"),
attachToResponse: stubMethod("attachToResponse"),
isNewArchitecture: () => false,
platform: Platform.OS,
};
};
// Create wrapper that works with both architectures
const createRNRefinerWrapper = (nativeModule) => {
if (!nativeModule) {
return createStubModule();
}
// Pass architecture information to native module
if (nativeModule.setArchitectureInfo) {
nativeModule.setArchitectureInfo(isNewArchitecture);
}
// Additional runtime detection if available (works for both Android and iOS)
if (nativeModule.detectArchitecture) {
nativeModule
.detectArchitecture()
.then((detectedArch) => {
// Architecture detection completed
})
.catch(() => {
// Ignore detection errors
});
}
return {
// Core SDK methods
initialize: (projectId, debugMode) => {
return nativeModule.initialize(projectId, debugMode);
},
setProject: (projectId) => nativeModule.setProject(projectId),
identifyUser: (userId, userTraits, locale, signature, writeOperation) => {
// Always pass an object for userTraits
const safeTraits = userTraits || {};
try {
if (isNewArchitecture) {
// TurboModule expects non-nullable userTraits
return nativeModule.identifyUser(
userId,
safeTraits,
locale,
signature,
writeOperation
);
} else {
// Legacy module can handle nullable userTraits
return nativeModule.identifyUser(
userId,
safeTraits,
locale,
signature,
writeOperation
);
}
} catch (error) {
throw error;
}
},
setUser: (userId, userTraits, locale, signature) => {
// Handle the difference between TurboModule (non-nullable) and legacy module (nullable)
if (isNewArchitecture) {
// TurboModule expects non-nullable userTraits, so we need to provide an empty object if null
const traits = userTraits || {};
return nativeModule.setUser(userId, traits, locale, signature);
} else {
// Legacy module can handle nullable userTraits
return nativeModule.setUser(
userId,
userTraits || null,
locale,
signature
);
}
},
resetUser: () => nativeModule.resetUser(),
trackEvent: (eventName) => nativeModule.trackEvent(eventName),
trackScreen: (screenName) => nativeModule.trackScreen(screenName),
ping: () => nativeModule.ping(),
showForm: (formUuid, force) => nativeModule.showForm(formUuid, force),
dismissForm: (formUuid) => nativeModule.dismissForm(formUuid),
closeForm: (formUuid) => nativeModule.closeForm(formUuid),
addToResponse: (contextualData) => {
// Handle the difference between TurboModule (non-nullable) and legacy module (nullable)
if (isNewArchitecture) {
// TurboModule expects non-nullable contextualData, so we need to provide an empty object if null
const data = contextualData || {};
return nativeModule.addToResponse(data);
} else {
// Legacy module can handle nullable contextualData
return nativeModule.addToResponse(contextualData || null);
}
},
startSession: () => nativeModule.startSession(),
// Event emitter methods (required for both architectures)
addListener: (eventName) => nativeModule.addListener(eventName),
removeListeners: (count) => nativeModule.removeListeners(count),
// Deprecated method for backward compatibility
attachToResponse: (contextualData) => {
// Handle the difference between TurboModule (non-nullable) and legacy module (nullable)
if (isNewArchitecture) {
// TurboModule expects non-nullable contextualData, so we need to provide an empty object if null
const data = contextualData || {};
return nativeModule.addToResponse(data);
} else {
// Legacy module can handle nullable contextualData
return nativeModule.addToResponse(contextualData || null);
}
},
// Architecture detection helper
isNewArchitecture: () => isNewArchitecture,
// Platform info
platform: Platform.OS,
};
};
// Export the wrapped module
export const RNRefiner = createRNRefinerWrapper(RNRefinerModule);
// Create event emitter that works with both architectures - with fallback for when module is unavailable
let RNRefinerEventEmitter;
try {
if (RNRefinerModule) {
RNRefinerEventEmitter = new NativeEventEmitter(RNRefinerModule);
} else {
// Create a stub event emitter when native module is not available
// Use a simple object with event emitter methods instead of Node.js events
RNRefinerEventEmitter = {
addListener: (eventName, callback) => {
return {
remove: () => {
// Stub remove method
},
};
},
removeAllListeners: (eventName) => {
// Stub removeAllListeners method
},
};
}
} catch (error) {
// Fallback to a basic stub implementation
RNRefinerEventEmitter = {
addListener: (eventName, callback) => {
return {
remove: () => {
// Stub remove method
},
};
},
removeAllListeners: (eventName) => {
// Stub removeAllListeners method
},
};
}
export { RNRefinerEventEmitter };
// Legacy export for backward compatibility
export default RNRefiner;