UNPKG

onboardsync-react-native

Version:

Expo SDK for OnboardSync - Remote onboarding configuration platform with A/B testing

248 lines (246 loc) 10.7 kB
"use strict"; 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