onboardsync-react-native
Version:
Expo SDK for OnboardSync - Remote onboarding configuration platform with A/B testing
248 lines (246 loc) • 10.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OnboardSync = exports.OnboardSyncComponent = void 0;
const react_1 = __importStar(require("react"));
const react_native_1 = require("react-native");
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
const Application = __importStar(require("expo-application"));
const WebViewScreen_1 = require("./screens/WebViewScreen");
const FallbackScreen_1 = require("./screens/FallbackScreen");
const deviceIdManager_1 = require("./utils/deviceIdManager");
const statusBarHelper_1 = require("./utils/statusBarHelper");
const colorUtils_1 = require("./utils/colorUtils");
const constants_1 = require("./constants");
let isWebViewActive = false;
const OnboardSyncComponent = ({ projectId, secretKey, testingEnabled = false, onComplete, }) => {
const [showModal, setShowModal] = (0, react_1.useState)(false);
const [modalConfig, setModalConfig] = (0, react_1.useState)(null);
(0, react_1.useEffect)(() => {
startOnboarding();
return () => {
if (isWebViewActive) {
isWebViewActive = false;
statusBarHelper_1.StatusBarHelper.restore();
}
};
}, []);
const startOnboarding = async () => {
try {
console.log('[OnboardSync] Starting onboarding flow');
if (isWebViewActive) {
console.log('[OnboardSync] WebView already active, skipping');
return;
}
if (!testingEnabled) {
const hasCompleted = await async_storage_1.default.getItem(constants_1.Constants.ONBOARDING_COMPLETED_KEY);
if (hasCompleted === 'true') {
console.log('[OnboardSync] Onboarding already completed');
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
return;
}
}
else {
console.log('[OnboardSync] Testing mode: ignoring completion status');
}
const appName = await getAppName();
const backgroundColor = '#FFFFFF';
try {
const backendDomain = await fetchBackendDomain({ projectId, secretKey });
const deviceId = await deviceIdManager_1.DeviceIDManager.getDeviceId();
const flowId = await resolveFlowId(backendDomain, projectId, deviceId);
const url = `${backendDomain}/onboarding/${flowId}/1?deviceId=${deviceId}`;
console.log(`[OnboardSync] Loading URL: ${url}`);
isWebViewActive = true;
statusBarHelper_1.StatusBarHelper.saveCurrentState();
statusBarHelper_1.StatusBarHelper.updateForColor(backgroundColor);
setModalConfig({
type: 'webview',
url,
appName,
backgroundColor,
testingEnabled,
});
setShowModal(true);
}
catch (error) {
console.error('[OnboardSync] Error:', error);
isWebViewActive = true;
statusBarHelper_1.StatusBarHelper.saveCurrentState();
statusBarHelper_1.StatusBarHelper.updateForColor(backgroundColor);
setModalConfig({
type: 'fallback',
appName,
backgroundColor,
});
setShowModal(true);
}
}
catch (error) {
console.error('[OnboardSync] Fatal error:', error);
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
}
};
const handleComplete = async (result) => {
var _a;
console.log(`[OnboardSync] Onboarding completed with ${(_a = result === null || result === void 0 ? void 0 : result.responses.length) !== null && _a !== void 0 ? _a : 0} responses`);
if (!testingEnabled) {
try {
await async_storage_1.default.setItem(constants_1.Constants.ONBOARDING_COMPLETED_KEY, 'true');
console.log('[OnboardSync] Saved completion status');
}
catch (error) {
console.error('[OnboardSync] Error saving completion status:', error);
}
}
setShowModal(false);
isWebViewActive = false;
statusBarHelper_1.StatusBarHelper.restore();
onComplete === null || onComplete === void 0 ? void 0 : onComplete(result);
};
const handleClose = () => {
console.log('[OnboardSync] Closing onboarding');
setShowModal(false);
isWebViewActive = false;
statusBarHelper_1.StatusBarHelper.restore();
};
if (!showModal || !modalConfig) {
return null;
}
return (<react_native_1.Modal visible={true} animationType="slide" presentationStyle="fullScreen" onRequestClose={handleClose}>
{modalConfig.type === 'fallback' ? (<FallbackScreen_1.FallbackScreen appName={modalConfig.appName} backgroundColor={modalConfig.backgroundColor} textColor={colorUtils_1.ColorUtils.getContrastColor(modalConfig.backgroundColor)} onClose={() => {
handleClose();
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
}}/>) : (<WebViewScreen_1.WebViewScreen url={modalConfig.url} testingEnabled={modalConfig.testingEnabled} appName={modalConfig.appName} backgroundColor={modalConfig.backgroundColor} onClose={handleClose} onComplete={handleComplete}/>)}
</react_native_1.Modal>);
};
exports.OnboardSyncComponent = OnboardSyncComponent;
class OnboardSync {
static showOnboarding(config) {
console.log('[OnboardSync] In React Native, please use <OnboardSyncComponent /> instead of OnboardSync.showOnboarding()');
console.log('[OnboardSync] Example:');
console.log(`
import { OnboardSyncComponent } from 'onboardsync-react-native';
// In your component:
const [showOnboarding, setShowOnboarding] = useState(true);
return (
<>
{showOnboarding && (
<OnboardSyncComponent
projectId="${config.projectId}"
secretKey="${config.secretKey}"
testingEnabled={${config.testingEnabled || false}}
onComplete={() => {
setShowOnboarding(false);
// Your completion logic
}}
/>
)}
<YourAppContent />
</>
);
`);
}
static async clearCompletionStatus() {
await async_storage_1.default.removeItem(constants_1.Constants.ONBOARDING_COMPLETED_KEY);
console.log('[OnboardSync] Cleared completion status');
}
}
exports.OnboardSync = OnboardSync;
async function getAppName() {
try {
const appName = Application.applicationName;
if (appName) {
return appName;
}
}
catch (error) {
console.log('[OnboardSync] Could not get app name:', error);
}
return constants_1.Constants.DEFAULT_APP_NAME;
}
async function fetchBackendDomain(config) {
const url = new URL(constants_1.Constants.GLOBAL_CONFIG_ENDPOINT);
url.searchParams.append('apiKey', config.secretKey);
url.searchParams.append('projectId', config.projectId);
console.log(`[OnboardSync] Fetching config from: ${url}`);
const response = await fetch(url.toString());
if (!response.ok) {
let errorMessage = `Config fetch failed: ${response.status}`;
try {
const errorBody = await response.json();
errorMessage = errorBody.error || errorMessage;
}
catch (_a) { }
throw new Error(errorMessage);
}
const data = await response.json();
if (!data.backendDomain) {
throw new Error('Empty backendDomain received from config');
}
console.log(`[OnboardSync] Backend domain: ${data.backendDomain}`);
return data.backendDomain;
}
async function resolveFlowId(backendDomain, projectId, deviceId) {
const url = new URL(`${backendDomain}/api/onboarding/resolve`);
url.searchParams.append('projectId', projectId);
url.searchParams.append('deviceId', deviceId);
console.log(`[OnboardSync] Resolving flow: ${url}`);
const response = await fetch(url.toString());
if (!response.ok) {
let errorMessage = `Flow resolution failed: ${response.status}`;
if (response.status === 404) {
errorMessage = 'No onboarding flow configured for this project (404)';
}
else {
try {
const errorBody = await response.json();
errorMessage = `Flow resolution failed: ${response.status} ${errorBody.error || ''}`;
}
catch (_a) { }
}
throw new Error(errorMessage);
}
const data = await response.json();
if (!data.flowId) {
throw new Error('Null flow ID received from API');
}
console.log(`[OnboardSync] Flow ID: ${data.flowId}`);
return data.flowId;
}
//# sourceMappingURL=OnboardSync.js.map