@namiml/expo-sdk 3.4.5-dev.202607092317 → 3.4.5-dev.202607101515
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/dist/index.cjs +96 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.mjs +97 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/index.ts +2 -1
- package/src/components/NamiView.tsx +6 -0
- package/src/components/TemplateRenderer.tsx +5 -0
- package/src/components/elements/NamiSegmentPicker.tsx +10 -1
- package/src/components/elements/NamiTextInput.tsx +156 -0
- package/src/components/elements/textInputStyle.ts +126 -0
- package/src/context/PaywallContext.tsx +13 -1
- package/src/index.ts +4 -0
- package/src/nami.ts +20 -2
- package/src/products/productStore.ts +63 -0
package/dist/index.cjs
CHANGED
|
@@ -292,6 +292,7 @@ class ExpoPurchaseAdapter {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
const expoUIAdapter = new ExpoUIAdapter();
|
|
295
|
+
const expoPurchaseAdapter = new ExpoPurchaseAdapter();
|
|
295
296
|
let adaptersRegistered = false;
|
|
296
297
|
function registerExpoAdapters() {
|
|
297
298
|
if (adaptersRegistered) {
|
|
@@ -303,7 +304,7 @@ function registerExpoAdapters() {
|
|
|
303
304
|
device: new ExpoDeviceAdapter(),
|
|
304
305
|
ui: expoUIAdapter,
|
|
305
306
|
});
|
|
306
|
-
sdkCore.registerPurchaseAdapter(
|
|
307
|
+
sdkCore.registerPurchaseAdapter(expoPurchaseAdapter);
|
|
307
308
|
adaptersRegistered = true;
|
|
308
309
|
}
|
|
309
310
|
catch (error) {
|
|
@@ -312,6 +313,79 @@ function registerExpoAdapters() {
|
|
|
312
313
|
}
|
|
313
314
|
registerExpoAdapters();
|
|
314
315
|
|
|
316
|
+
// Accumulated store product details, keyed by product_ref_id (== sku_ref_id).
|
|
317
|
+
const productsById = new Map();
|
|
318
|
+
// Serializes backfill runs so concurrent launches/prefetches don't double-fetch
|
|
319
|
+
// or race the map (parity with the Apple SDK's backfillTaskLock).
|
|
320
|
+
let pending = Promise.resolve(false);
|
|
321
|
+
/** Collect unique store product ids (sku_ref_id) from the given paywalls' sku menus. */
|
|
322
|
+
function collectSkuRefIds(paywalls) {
|
|
323
|
+
const ids = new Set();
|
|
324
|
+
for (const paywall of paywalls ?? []) {
|
|
325
|
+
for (const menu of paywall?.sku_menus ?? []) {
|
|
326
|
+
for (const sku of menu?.products ?? []) {
|
|
327
|
+
if (sku?.sku_ref_id)
|
|
328
|
+
ids.add(sku.sku_ref_id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return [...ids];
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Fetch store product details for any of `skuRefIds` not already loaded, merge them into
|
|
336
|
+
* the accumulated set, and push the union to core via NamiPaywallManager.setProductDetails.
|
|
337
|
+
* Serialized; returns whether new products were added. Never throws.
|
|
338
|
+
*/
|
|
339
|
+
function backfill(skuRefIds) {
|
|
340
|
+
pending = pending.then(() => backfillOnce(skuRefIds));
|
|
341
|
+
return pending;
|
|
342
|
+
}
|
|
343
|
+
async function backfillOnce(skuRefIds) {
|
|
344
|
+
const missing = [...new Set(skuRefIds)].filter((id) => id && !productsById.has(id));
|
|
345
|
+
if (!missing.length)
|
|
346
|
+
return false;
|
|
347
|
+
try {
|
|
348
|
+
const products = await expoPurchaseAdapter.getProducts(missing);
|
|
349
|
+
let added = false;
|
|
350
|
+
for (const product of products ?? []) {
|
|
351
|
+
if (product?.product_ref_id) {
|
|
352
|
+
productsById.set(product.product_ref_id, product);
|
|
353
|
+
added = true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (added) {
|
|
357
|
+
sdkCore.NamiPaywallManager.setProductDetails([...productsById.values()]);
|
|
358
|
+
}
|
|
359
|
+
return added;
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
sdkCore.logger.warn('[NamiExpo] Failed to load store products.', error);
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/** Clear accumulated products. Called on Nami.reset so reconfigure doesn't serve stale data. */
|
|
367
|
+
function reset() {
|
|
368
|
+
pending = pending.then(() => {
|
|
369
|
+
productsById.clear();
|
|
370
|
+
return false;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Prefetch StoreKit/Play product details for every configured paywall's SKUs so that
|
|
376
|
+
* prices are available before any paywall mounts (mirrors the Apple SDK startup load).
|
|
377
|
+
* Fire-and-forget and error-safe: a failure here never blocks configure.
|
|
378
|
+
*/
|
|
379
|
+
async function prefetchProducts() {
|
|
380
|
+
try {
|
|
381
|
+
const ids = collectSkuRefIds(sdkCore.NamiPaywallManager.allPaywalls());
|
|
382
|
+
if (ids.length)
|
|
383
|
+
await backfill(ids);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
sdkCore.logger.warn('[NamiExpo] Product prefetch skipped.', error);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
315
389
|
class Nami {
|
|
316
390
|
static get sdkVersion() {
|
|
317
391
|
return sdkCore.Nami.sdkVersion();
|
|
@@ -320,9 +394,12 @@ class Nami {
|
|
|
320
394
|
return sdkCore.Nami.sdkPackageVersion();
|
|
321
395
|
}
|
|
322
396
|
static async configure(config) {
|
|
323
|
-
|
|
397
|
+
const result = await sdkCore.Nami.configure(config);
|
|
398
|
+
void prefetchProducts();
|
|
399
|
+
return result;
|
|
324
400
|
}
|
|
325
401
|
static async reset() {
|
|
402
|
+
reset();
|
|
326
403
|
await sdkCore.Nami.reset?.();
|
|
327
404
|
}
|
|
328
405
|
}
|
|
@@ -1796,6 +1873,15 @@ const PaywallProvider = ({ paywall, context, campaign, flow, onFirstFocusReady,
|
|
|
1796
1873
|
setFormState: (formId, value) => {
|
|
1797
1874
|
providerRef.current?.setFormState(formId, value);
|
|
1798
1875
|
},
|
|
1876
|
+
registerFormFieldValidator: (formId, validator) => {
|
|
1877
|
+
providerRef.current?.registerFormFieldValidator(formId, validator);
|
|
1878
|
+
},
|
|
1879
|
+
setFormFieldError: (formId, message) => {
|
|
1880
|
+
providerRef.current?.setFormFieldError(formId, message);
|
|
1881
|
+
},
|
|
1882
|
+
clearFormFieldError: (formId) => {
|
|
1883
|
+
providerRef.current?.clearFormFieldError(formId);
|
|
1884
|
+
},
|
|
1799
1885
|
setTimerState: (timerId, remainingSeconds, savedAt, hasEmittedCompletion) => {
|
|
1800
1886
|
providerRef.current?.setTimerState(timerId, remainingSeconds, savedAt, hasEmittedCompletion);
|
|
1801
1887
|
},
|
|
@@ -2812,6 +2898,9 @@ function resolveRenderer(rendererKey) {
|
|
|
2812
2898
|
case 'NamiToggleSwitch':
|
|
2813
2899
|
renderer = require('./elements/NamiToggleSwitch').NamiToggleSwitch;
|
|
2814
2900
|
break;
|
|
2901
|
+
case 'NamiTextInput':
|
|
2902
|
+
renderer = require('./elements/NamiTextInput').NamiTextInput;
|
|
2903
|
+
break;
|
|
2815
2904
|
case 'NamiRadioButton':
|
|
2816
2905
|
renderer = require('./elements/NamiRadioButton').NamiRadioButton;
|
|
2817
2906
|
break;
|
|
@@ -2919,6 +3008,8 @@ function renderResolvedComponent(componentType, resolvedComponent, scaleFactor,
|
|
|
2919
3008
|
return renderWithKey('NamiSegmentPickerItem', sharedProps);
|
|
2920
3009
|
case 'toggleSwitch':
|
|
2921
3010
|
return renderWithKey('NamiToggleSwitch', sharedProps);
|
|
3011
|
+
case 'textInput':
|
|
3012
|
+
return renderWithKey('NamiTextInput', sharedProps);
|
|
2922
3013
|
case 'radio':
|
|
2923
3014
|
return renderWithKey('NamiRadioButton', sharedProps);
|
|
2924
3015
|
case 'progressIndicator':
|
|
@@ -3692,6 +3783,9 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
|
|
|
3692
3783
|
launchCacheRef.current.set(cacheKey, nextLaunch);
|
|
3693
3784
|
}
|
|
3694
3785
|
prewarmPaywallFonts([paywall, ...flowPaywalls]);
|
|
3786
|
+
const launchSkuRefIds = collectSkuRefIds([paywall, ...flowPaywalls].filter((item) => item != null));
|
|
3787
|
+
if (launchSkuRefIds.length)
|
|
3788
|
+
void backfill(launchSkuRefIds);
|
|
3695
3789
|
if (initialPaywall?.fonts) {
|
|
3696
3790
|
void prepareAndLoadFontsWithTimeout(initialPaywall.fonts);
|
|
3697
3791
|
}
|