rampkit-expo-dev 0.0.55 → 0.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/RampkitOverlay.js +100 -94
- package/package.json +1 -1
package/build/RampkitOverlay.js
CHANGED
|
@@ -1016,8 +1016,8 @@ function Overlay(props) {
|
|
|
1016
1016
|
const pagerTranslateX = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
1017
1017
|
const allLoaded = loadedCount >= props.screens.length;
|
|
1018
1018
|
const hasTrackedInitialScreen = (0, react_1.useRef)(false);
|
|
1019
|
-
// shared vars across all webviews
|
|
1020
|
-
const varsRef = (0, react_1.useRef)({});
|
|
1019
|
+
// shared vars across all webviews - INITIALIZE from props.variables!
|
|
1020
|
+
const varsRef = (0, react_1.useRef)(props.variables || {});
|
|
1021
1021
|
// hold refs for injection
|
|
1022
1022
|
const webviewsRef = (0, react_1.useRef)([]);
|
|
1023
1023
|
// Track when we last SENT vars to each page (for stale value filtering)
|
|
@@ -1025,6 +1025,14 @@ function Overlay(props) {
|
|
|
1025
1025
|
const lastVarsSendTimeRef = (0, react_1.useRef)([]);
|
|
1026
1026
|
// Stale value window in milliseconds - matches iOS SDK (600ms)
|
|
1027
1027
|
const STALE_VALUE_WINDOW_MS = 600;
|
|
1028
|
+
// Track which screens have completed initial setup (to avoid repeated onLoadEnd processing)
|
|
1029
|
+
const initializedScreensRef = (0, react_1.useRef)(new Set());
|
|
1030
|
+
// Track the currently active screen index (matches iOS SDK's activeScreenIndex)
|
|
1031
|
+
const activeScreenIndexRef = (0, react_1.useRef)(0);
|
|
1032
|
+
// Track when a screen was activated (to filter out stale echoes during settling period)
|
|
1033
|
+
const screenActivationTimeRef = (0, react_1.useRef)({ 0: Date.now() });
|
|
1034
|
+
// Settling period - ignore variable updates from a screen for this long after activation
|
|
1035
|
+
const SCREEN_SETTLING_MS = 300;
|
|
1028
1036
|
// ============================================================================
|
|
1029
1037
|
// Navigation Resolution Helpers (matches iOS SDK behavior)
|
|
1030
1038
|
// ============================================================================
|
|
@@ -1205,6 +1213,9 @@ function Overlay(props) {
|
|
|
1205
1213
|
// Slide animation: use PagerView's built-in animated page change
|
|
1206
1214
|
// and skip the fade curtain overlay.
|
|
1207
1215
|
if (animationType === "slide") {
|
|
1216
|
+
// Update active screen index and activation time FIRST
|
|
1217
|
+
activeScreenIndexRef.current = nextIndex;
|
|
1218
|
+
screenActivationTimeRef.current[nextIndex] = Date.now();
|
|
1208
1219
|
// @ts-ignore: methods exist on PagerView instance
|
|
1209
1220
|
const pager = pagerRef.current;
|
|
1210
1221
|
if (!pager)
|
|
@@ -1216,7 +1227,6 @@ function Overlay(props) {
|
|
|
1216
1227
|
pager.setPageWithoutAnimation(nextIndex);
|
|
1217
1228
|
}
|
|
1218
1229
|
// Explicitly send vars to the new page after setting it
|
|
1219
|
-
// This ensures the webview receives the latest state
|
|
1220
1230
|
requestAnimationFrame(() => {
|
|
1221
1231
|
sendVarsToWebView(nextIndex);
|
|
1222
1232
|
sendOnboardingStateToWebView(nextIndex);
|
|
@@ -1227,6 +1237,9 @@ function Overlay(props) {
|
|
|
1227
1237
|
// Animates the PagerView container out, switches page, then animates back in
|
|
1228
1238
|
if (animationType === "slidefade") {
|
|
1229
1239
|
setIsTransitioning(true);
|
|
1240
|
+
// Update active screen index and activation time FIRST
|
|
1241
|
+
activeScreenIndexRef.current = nextIndex;
|
|
1242
|
+
screenActivationTimeRef.current[nextIndex] = Date.now();
|
|
1230
1243
|
// Determine direction: forward (nextIndex > index) or backward
|
|
1231
1244
|
const isForward = nextIndex > index;
|
|
1232
1245
|
const direction = isForward ? 1 : -1;
|
|
@@ -1278,6 +1291,9 @@ function Overlay(props) {
|
|
|
1278
1291
|
}
|
|
1279
1292
|
// Default fade animation: uses a white curtain overlay
|
|
1280
1293
|
setIsTransitioning(true);
|
|
1294
|
+
// Update active screen index and activation time FIRST
|
|
1295
|
+
activeScreenIndexRef.current = nextIndex;
|
|
1296
|
+
screenActivationTimeRef.current[nextIndex] = Date.now();
|
|
1281
1297
|
react_native_1.Animated.timing(fadeOpacity, {
|
|
1282
1298
|
toValue: 1,
|
|
1283
1299
|
duration: 160,
|
|
@@ -1320,30 +1336,45 @@ function Overlay(props) {
|
|
|
1320
1336
|
try {
|
|
1321
1337
|
var payload = ${json};
|
|
1322
1338
|
var newVars = payload.vars;
|
|
1323
|
-
|
|
1324
|
-
|
|
1339
|
+
|
|
1340
|
+
// Update ALL variable storage locations for consistency
|
|
1341
|
+
// This ensures dynamic tap handlers can find the latest values
|
|
1342
|
+
window.__rampkitVariables = Object.assign({}, window.__rampkitVariables || {}, newVars);
|
|
1343
|
+
|
|
1344
|
+
// Also update window.__rampkitVars (used by template resolver and dynamic tap)
|
|
1345
|
+
if (window.__rampkitVars) {
|
|
1346
|
+
Object.keys(newVars).forEach(function(k) {
|
|
1347
|
+
window.__rampkitVars[k] = newVars[k];
|
|
1348
|
+
});
|
|
1349
|
+
} else {
|
|
1350
|
+
window.__rampkitVars = Object.assign({}, newVars);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// Also update RK_VARS if it exists (fallback storage)
|
|
1354
|
+
if (window.RK_VARS) {
|
|
1355
|
+
Object.keys(newVars).forEach(function(k) {
|
|
1356
|
+
window.RK_VARS[k] = newVars[k];
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1325
1360
|
// Call the handler if available
|
|
1326
1361
|
if (typeof window.__rkHandleVarsUpdate === 'function') {
|
|
1327
1362
|
window.__rkHandleVarsUpdate(newVars);
|
|
1328
1363
|
}
|
|
1329
|
-
|
|
1364
|
+
|
|
1365
|
+
// Dispatch MessageEvent to trigger template resolver
|
|
1330
1366
|
try {
|
|
1331
1367
|
document.dispatchEvent(new MessageEvent('message', {data: payload}));
|
|
1332
1368
|
} catch(e) {}
|
|
1333
|
-
|
|
1334
|
-
try {
|
|
1335
|
-
window.dispatchEvent(new MessageEvent('message', {data: payload}));
|
|
1336
|
-
} catch(e) {}
|
|
1369
|
+
|
|
1337
1370
|
// Also dispatch custom event for any listeners
|
|
1338
1371
|
try {
|
|
1339
1372
|
document.dispatchEvent(new CustomEvent('rampkit:vars-updated', {detail: newVars}));
|
|
1340
1373
|
} catch(e) {}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
window.onRampkitVarsUpdate(newVars);
|
|
1344
|
-
}
|
|
1374
|
+
|
|
1375
|
+
console.log('[RampKit] Variables updated:', Object.keys(newVars).length, 'keys');
|
|
1345
1376
|
} catch(e) {
|
|
1346
|
-
console.log('[
|
|
1377
|
+
console.log('[RampKit] buildDirectVarsScript error:', e);
|
|
1347
1378
|
}
|
|
1348
1379
|
})();`;
|
|
1349
1380
|
}
|
|
@@ -1415,34 +1446,6 @@ function Overlay(props) {
|
|
|
1415
1446
|
// because the WebView echoes back variables which triggers another sendVarsToWebView.
|
|
1416
1447
|
// Onboarding state is sent separately in onLoadEnd and onPageSelected.
|
|
1417
1448
|
}
|
|
1418
|
-
/**
|
|
1419
|
-
* Broadcast variables to all WebViews, optionally excluding one.
|
|
1420
|
-
* This mirrors the iOS SDK's broadcastVariables(excluding:) pattern.
|
|
1421
|
-
* @param excludeIndex - Optional index of WebView to skip (typically the source of the update)
|
|
1422
|
-
*/
|
|
1423
|
-
function broadcastVars(excludeIndex) {
|
|
1424
|
-
if (__DEV__)
|
|
1425
|
-
console.log("[Rampkit] broadcastVars", {
|
|
1426
|
-
recipients: webviewsRef.current.length,
|
|
1427
|
-
excludeIndex,
|
|
1428
|
-
vars: varsRef.current,
|
|
1429
|
-
});
|
|
1430
|
-
const script = buildDirectVarsScript(varsRef.current);
|
|
1431
|
-
const now = Date.now();
|
|
1432
|
-
for (let i = 0; i < webviewsRef.current.length; i++) {
|
|
1433
|
-
// Skip the source WebView to prevent echo loops
|
|
1434
|
-
if (excludeIndex !== undefined && i === excludeIndex) {
|
|
1435
|
-
continue;
|
|
1436
|
-
}
|
|
1437
|
-
const wv = webviewsRef.current[i];
|
|
1438
|
-
if (wv) {
|
|
1439
|
-
// Track send time for stale value filtering
|
|
1440
|
-
lastVarsSendTimeRef.current[i] = now;
|
|
1441
|
-
// @ts-ignore: injectJavaScript exists on WebView instance
|
|
1442
|
-
wv.injectJavaScript(script);
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
1449
|
react_1.default.useEffect(() => {
|
|
1447
1450
|
const sub = react_native_1.BackHandler.addEventListener("hardwareBackPress", () => {
|
|
1448
1451
|
if (index > 0) {
|
|
@@ -1487,25 +1490,16 @@ function Overlay(props) {
|
|
|
1487
1490
|
const onPageSelected = (e) => {
|
|
1488
1491
|
const pos = e.nativeEvent.position;
|
|
1489
1492
|
setIndex(pos);
|
|
1490
|
-
//
|
|
1493
|
+
// Update active screen index and activation time FIRST
|
|
1494
|
+
activeScreenIndexRef.current = pos;
|
|
1495
|
+
screenActivationTimeRef.current[pos] = Date.now();
|
|
1491
1496
|
if (__DEV__)
|
|
1492
|
-
console.log("[Rampkit] onPageSelected", pos);
|
|
1493
|
-
// Send vars
|
|
1494
|
-
// receives them. The first send might fail if the webview isn't fully ready,
|
|
1495
|
-
// so we retry a few times.
|
|
1497
|
+
console.log("[Rampkit] onPageSelected - activating screen", pos);
|
|
1498
|
+
// Send vars and onboarding state to the newly active screen
|
|
1496
1499
|
requestAnimationFrame(() => {
|
|
1497
1500
|
sendVarsToWebView(pos);
|
|
1498
|
-
// Send onboarding state once after vars
|
|
1499
1501
|
sendOnboardingStateToWebView(pos);
|
|
1500
1502
|
});
|
|
1501
|
-
// Retry after a short delay in case the first send didn't work
|
|
1502
|
-
setTimeout(() => {
|
|
1503
|
-
sendVarsToWebView(pos);
|
|
1504
|
-
}, 50);
|
|
1505
|
-
// Final retry to catch any edge cases
|
|
1506
|
-
setTimeout(() => {
|
|
1507
|
-
sendVarsToWebView(pos);
|
|
1508
|
-
}, 150);
|
|
1509
1503
|
// Track screen change event
|
|
1510
1504
|
if (props.onScreenChange && props.screens[pos]) {
|
|
1511
1505
|
props.onScreenChange(pos, props.screens[pos].id);
|
|
@@ -1606,7 +1600,7 @@ function Overlay(props) {
|
|
|
1606
1600
|
(_b = props.onNotificationPermissionResult) === null || _b === void 0 ? void 0 : _b.call(props, !!(result === null || result === void 0 ? void 0 : result.granted));
|
|
1607
1601
|
}
|
|
1608
1602
|
catch (_) { }
|
|
1609
|
-
// Save to shared vars and
|
|
1603
|
+
// Save to shared vars and send to active screen only
|
|
1610
1604
|
try {
|
|
1611
1605
|
varsRef.current = {
|
|
1612
1606
|
...varsRef.current,
|
|
@@ -1617,7 +1611,8 @@ function Overlay(props) {
|
|
|
1617
1611
|
ios: result === null || result === void 0 ? void 0 : result.ios,
|
|
1618
1612
|
},
|
|
1619
1613
|
};
|
|
1620
|
-
|
|
1614
|
+
// Only send to active screen to avoid broadcast loops
|
|
1615
|
+
sendVarsToWebView(activeScreenIndexRef.current);
|
|
1621
1616
|
}
|
|
1622
1617
|
catch (_) { }
|
|
1623
1618
|
}
|
|
@@ -1632,6 +1627,13 @@ function Overlay(props) {
|
|
|
1632
1627
|
transform: [{ translateX: pagerTranslateX }],
|
|
1633
1628
|
},
|
|
1634
1629
|
], children: (0, jsx_runtime_1.jsx)(react_native_pager_view_1.default, { ref: pagerRef, style: react_native_1.StyleSheet.absoluteFill, scrollEnabled: false, initialPage: 0, onPageSelected: onPageSelected, offscreenPageLimit: props.screens.length, overScrollMode: "never", children: docs.map((doc, i) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.page, renderToHardwareTextureAndroid: true, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { ref: (r) => (webviewsRef.current[i] = r), style: styles.webview, originWhitelist: ["*"], source: { html: doc }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening + exports.injectedDynamicTapHandler + exports.injectedButtonAnimations, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedButtonAnimations, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, overScrollMode: "never", scalesPageToFit: false, showsHorizontalScrollIndicator: false, dataDetectorTypes: "none", allowsLinkPreview: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, javaScriptEnabled: true, domStorageEnabled: true, hideKeyboardAccessoryView: true, onLoadEnd: () => {
|
|
1630
|
+
// Only initialize each screen ONCE to avoid repeated processing
|
|
1631
|
+
if (initializedScreensRef.current.has(i)) {
|
|
1632
|
+
if (__DEV__)
|
|
1633
|
+
console.log(`[Rampkit] onLoadEnd skipped (already initialized): ${i}`);
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
initializedScreensRef.current.add(i);
|
|
1635
1637
|
setLoadedCount((c) => c + 1);
|
|
1636
1638
|
if (i === 0) {
|
|
1637
1639
|
setFirstPageLoaded(true);
|
|
@@ -1641,12 +1643,14 @@ function Overlay(props) {
|
|
|
1641
1643
|
props.onScreenChange(0, props.screens[0].id);
|
|
1642
1644
|
}
|
|
1643
1645
|
}
|
|
1644
|
-
// Initialize this page with current vars
|
|
1646
|
+
// Initialize this page with current vars
|
|
1645
1647
|
if (__DEV__)
|
|
1646
|
-
console.log("[Rampkit] onLoadEnd
|
|
1648
|
+
console.log("[Rampkit] onLoadEnd initializing screen", i);
|
|
1647
1649
|
sendVarsToWebView(i, true);
|
|
1648
|
-
//
|
|
1649
|
-
|
|
1650
|
+
// Only send onboarding state to the ACTIVE screen (index 0 on initial load)
|
|
1651
|
+
if (i === activeScreenIndexRef.current) {
|
|
1652
|
+
sendOnboardingStateToWebView(i);
|
|
1653
|
+
}
|
|
1650
1654
|
}, onMessage: (ev) => {
|
|
1651
1655
|
var _a, _b, _c, _d;
|
|
1652
1656
|
const raw = ev.nativeEvent.data;
|
|
@@ -1655,53 +1659,52 @@ function Overlay(props) {
|
|
|
1655
1659
|
try {
|
|
1656
1660
|
// JSON path
|
|
1657
1661
|
const data = JSON.parse(raw);
|
|
1658
|
-
// 1) Variables from a page → update shared
|
|
1659
|
-
//
|
|
1662
|
+
// 1) Variables from a page → update shared state
|
|
1663
|
+
// CRITICAL: Only accept variable updates from the ACTIVE screen
|
|
1664
|
+
// This prevents inactive screens from causing infinite broadcast loops
|
|
1660
1665
|
if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:variables" &&
|
|
1661
1666
|
(data === null || data === void 0 ? void 0 : data.vars) &&
|
|
1662
1667
|
typeof data.vars === "object") {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1668
|
+
// CRITICAL: Ignore variable updates from non-active screens
|
|
1669
|
+
// Only the currently visible screen should be able to update variables
|
|
1670
|
+
if (i !== activeScreenIndexRef.current) {
|
|
1671
|
+
if (__DEV__) {
|
|
1672
|
+
console.log(`[Rampkit] ignoring variables from inactive screen ${i} (active: ${activeScreenIndexRef.current})`);
|
|
1673
|
+
}
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1667
1676
|
const now = Date.now();
|
|
1677
|
+
// Check if this screen is still in the settling period after activation
|
|
1678
|
+
// During settling, we filter more aggressively to prevent stale echoes
|
|
1679
|
+
const activationTime = screenActivationTimeRef.current[i] || 0;
|
|
1680
|
+
const timeSinceActivation = now - activationTime;
|
|
1681
|
+
const isSettling = timeSinceActivation < SCREEN_SETTLING_MS;
|
|
1682
|
+
// Check if we recently sent vars to this page
|
|
1668
1683
|
const lastSendTime = lastVarsSendTimeRef.current[i] || 0;
|
|
1669
1684
|
const timeSinceSend = now - lastSendTime;
|
|
1670
1685
|
const isWithinStaleWindow = timeSinceSend < STALE_VALUE_WINDOW_MS;
|
|
1671
|
-
if (__DEV__)
|
|
1672
|
-
console.log("[Rampkit]
|
|
1673
|
-
pageIndex: i,
|
|
1674
|
-
isWithinStaleWindow,
|
|
1675
|
-
timeSinceSend,
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1686
|
+
if (__DEV__)
|
|
1687
|
+
console.log("[Rampkit] received variables from ACTIVE page", i, { isSettling, timeSinceActivation, isWithinStaleWindow, timeSinceSend });
|
|
1678
1688
|
let changed = false;
|
|
1679
1689
|
const newVars = {};
|
|
1680
1690
|
for (const [key, value] of Object.entries(data.vars)) {
|
|
1681
1691
|
// CRITICAL: Filter out onboarding.* variables
|
|
1682
|
-
// These are read-only from the WebView's perspective
|
|
1683
|
-
// controlled by the SDK. Accepting them back creates infinite loops.
|
|
1692
|
+
// These are read-only from the WebView's perspective
|
|
1684
1693
|
if (key.startsWith('onboarding.')) {
|
|
1685
|
-
if (__DEV__) {
|
|
1686
|
-
console.log(`[Rampkit] ignoring read-only onboarding variable: ${key}`);
|
|
1687
|
-
}
|
|
1688
1694
|
continue;
|
|
1689
1695
|
}
|
|
1690
1696
|
const hasHostVal = Object.prototype.hasOwnProperty.call(varsRef.current, key);
|
|
1691
1697
|
const hostVal = varsRef.current[key];
|
|
1692
|
-
//
|
|
1693
|
-
//
|
|
1694
|
-
|
|
1695
|
-
// This prevents pages from clobbering user input with cached defaults
|
|
1696
|
-
// when they first become active/visible.
|
|
1697
|
-
if (isWithinStaleWindow && hasHostVal) {
|
|
1698
|
+
// During settling period OR stale window: protect non-empty values
|
|
1699
|
+
// This prevents the screen from clobbering user input with defaults
|
|
1700
|
+
if ((isSettling || isWithinStaleWindow) && hasHostVal) {
|
|
1698
1701
|
const hostIsNonEmpty = hostVal !== "" && hostVal !== null && hostVal !== undefined;
|
|
1699
1702
|
const incomingIsEmpty = value === "" || value === null || value === undefined;
|
|
1700
1703
|
if (hostIsNonEmpty && incomingIsEmpty) {
|
|
1701
1704
|
if (__DEV__) {
|
|
1702
|
-
console.log(`[Rampkit]
|
|
1705
|
+
console.log(`[Rampkit] protecting value for "${key}": "${hostVal}" (settling: ${isSettling})`);
|
|
1703
1706
|
}
|
|
1704
|
-
continue; // Skip
|
|
1707
|
+
continue; // Skip - keep existing non-empty value
|
|
1705
1708
|
}
|
|
1706
1709
|
}
|
|
1707
1710
|
// Accept the update if value is different
|
|
@@ -1712,12 +1715,15 @@ function Overlay(props) {
|
|
|
1712
1715
|
}
|
|
1713
1716
|
if (changed) {
|
|
1714
1717
|
varsRef.current = { ...varsRef.current, ...newVars };
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
+
if (__DEV__) {
|
|
1719
|
+
console.log("[Rampkit] variables updated:", newVars);
|
|
1720
|
+
}
|
|
1721
|
+
// CRITICAL: Send merged vars back to the active screen
|
|
1722
|
+
// This ensures window.__rampkitVariables has the complete state
|
|
1723
|
+
// which is needed for dynamic tap conditions to evaluate correctly
|
|
1724
|
+
// Only send if there were actual changes to prevent echo loops
|
|
1725
|
+
sendVarsToWebView(i);
|
|
1718
1726
|
}
|
|
1719
|
-
// NOTE: Do NOT send vars back to source page - it already has them
|
|
1720
|
-
// and would just echo them back again, creating a ping-pong loop
|
|
1721
1727
|
return;
|
|
1722
1728
|
}
|
|
1723
1729
|
// 2) A page asked for current vars → send only to that page
|
package/package.json
CHANGED