@namiml/expo-sdk 3.4.0-dev.202605060437
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 +4000 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.mjs +3966 -0
- package/dist/index.mjs.map +1 -0
- package/nami-expo-nami-iap.tgz +0 -0
- package/package.json +92 -0
- package/src/adapters/expo-device.adapter.ts +106 -0
- package/src/adapters/expo-purchase.adapter.ts +79 -0
- package/src/adapters/expo-storage.adapter.ts +92 -0
- package/src/adapters/expo-ui.adapter.ts +57 -0
- package/src/adapters/index.ts +33 -0
- package/src/amazon-kepler.d.ts +7 -0
- package/src/components/NamiView.tsx +1006 -0
- package/src/components/PaywallScreen.tsx +245 -0
- package/src/components/TemplateRenderer.tsx +243 -0
- package/src/components/containers/NamiBackgroundContainer.tsx +103 -0
- package/src/components/containers/NamiCarousel.tsx +217 -0
- package/src/components/containers/NamiCollapseContainer.tsx +116 -0
- package/src/components/containers/NamiContainer.tsx +315 -0
- package/src/components/containers/NamiContentContainer.tsx +140 -0
- package/src/components/containers/NamiFooter.tsx +35 -0
- package/src/components/containers/NamiHeader.tsx +45 -0
- package/src/components/containers/NamiProductContainer.tsx +248 -0
- package/src/components/containers/NamiRepeatingGrid.tsx +81 -0
- package/src/components/containers/NamiResponsiveGrid.tsx +75 -0
- package/src/components/containers/NamiStack.tsx +69 -0
- package/src/components/elements/NamiButton.tsx +285 -0
- package/src/components/elements/NamiCountdownTimer.tsx +123 -0
- package/src/components/elements/NamiImage.tsx +177 -0
- package/src/components/elements/NamiPlayPauseButton.tsx +93 -0
- package/src/components/elements/NamiProgressBar.tsx +90 -0
- package/src/components/elements/NamiProgressIndicator.tsx +41 -0
- package/src/components/elements/NamiQRCode.tsx +51 -0
- package/src/components/elements/NamiRadioButton.tsx +62 -0
- package/src/components/elements/NamiSegmentPicker.tsx +67 -0
- package/src/components/elements/NamiSegmentPickerItem.tsx +184 -0
- package/src/components/elements/NamiSpacer.tsx +23 -0
- package/src/components/elements/NamiSymbol.tsx +104 -0
- package/src/components/elements/NamiText.tsx +311 -0
- package/src/components/elements/NamiToggleButton.tsx +102 -0
- package/src/components/elements/NamiToggleSwitch.tsx +64 -0
- package/src/components/elements/NamiVideo.kepler.tsx +638 -0
- package/src/components/elements/NamiVideo.tsx +133 -0
- package/src/components/elements/NamiVolumeButton.tsx +93 -0
- package/src/context/FocusContext.tsx +169 -0
- package/src/context/PaywallContext.tsx +343 -0
- package/src/global.d.ts +5 -0
- package/src/index.ts +62 -0
- package/src/nami.ts +24 -0
- package/src/react-native-qrcode-svg.d.ts +4 -0
- package/src/utils/actionHandler.ts +281 -0
- package/src/utils/fonts.ts +359 -0
- package/src/utils/iconMap.ts +67 -0
- package/src/utils/impression.ts +39 -0
- package/src/utils/rendering.ts +197 -0
- package/src/utils/smartText.ts +148 -0
- package/src/utils/styles.ts +668 -0
- package/src/utils/tvFocus.ts +31 -0
- package/src/utils/videoControls.ts +49 -0
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@namiml/expo-sdk",
|
|
3
|
+
"version": "3.4.0-dev.202605060437",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Nami Expo SDK — paywall and subscription management for Expo apps",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"react-native": "./src/index.ts",
|
|
10
|
+
"source": "./src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"nami-sdk-core.tgz",
|
|
22
|
+
"nami-expo-nami-iap.tgz"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "NAMI_SDK_ENV=development rollup -c",
|
|
26
|
+
"build:prod": "NAMI_SDK_ENV=production rollup -c",
|
|
27
|
+
"test": "jest --coverage",
|
|
28
|
+
"lint": "eslint src/",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"dev": "rollup -c -w",
|
|
31
|
+
"prepublishOnly": "yarn clean && yarn build:prod"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@namiml/expo-nami-iap": "3.4.0-dev.202605060437",
|
|
35
|
+
"@namiml/sdk-core": "3.4.0-dev.202605060437",
|
|
36
|
+
"@amazon-devices/expo-asset": "~2.0.0",
|
|
37
|
+
"@amazon-devices/react-native-kepler": "^2.0.1758683737",
|
|
38
|
+
"react-native-qrcode-svg": "^6.3.21",
|
|
39
|
+
"react-native-svg": "^15.15.4"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"expo-constants": "*",
|
|
43
|
+
"expo-device": "*",
|
|
44
|
+
"expo-font": "*",
|
|
45
|
+
"expo-image": "*",
|
|
46
|
+
"expo-linking": "*",
|
|
47
|
+
"expo-localization": "*",
|
|
48
|
+
"expo-sqlite": "*",
|
|
49
|
+
"expo-video": "*",
|
|
50
|
+
"react": ">=18.0.0",
|
|
51
|
+
"react-native": ">=0.72.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"expo-font": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"expo-image": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"expo-video": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"react-native-iap": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@rollup/plugin-commonjs": "^28.0.0",
|
|
70
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
71
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
72
|
+
"@rollup/plugin-replace": "^6.0.0",
|
|
73
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
74
|
+
"@types/jest": "^29.5.14",
|
|
75
|
+
"@types/react": "^18.3.12",
|
|
76
|
+
"eslint": "^10.2.0",
|
|
77
|
+
"jest": "^29.7.0",
|
|
78
|
+
"jest-util": "^30.3.0",
|
|
79
|
+
"react": "^18.3.1",
|
|
80
|
+
"react-native": "^0.75.0",
|
|
81
|
+
"rollup": "^4.18.0",
|
|
82
|
+
"rollup-plugin-dts": "^6.1.0",
|
|
83
|
+
"ts-jest": "^29.2.0",
|
|
84
|
+
"typescript": "^5.4.5",
|
|
85
|
+
"typescript-eslint": "^8.58.0"
|
|
86
|
+
},
|
|
87
|
+
"packageManager": "yarn@4.4.0",
|
|
88
|
+
"sideEffects": [
|
|
89
|
+
"./src/index.*",
|
|
90
|
+
"./src/adapters/*"
|
|
91
|
+
]
|
|
92
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { IDeviceAdapter, ScreenInfo } from '@namiml/sdk-core';
|
|
2
|
+
import type { DevicePayload, TDevice } from '@namiml/sdk-core';
|
|
3
|
+
import { NAMI_SDK_PACKAGE_VERSION } from '@namiml/sdk-core';
|
|
4
|
+
import { Dimensions, Platform } from 'react-native';
|
|
5
|
+
|
|
6
|
+
function getExpoDevice() { return require('expo-device'); }
|
|
7
|
+
function getExpoConstants() { return require('expo-constants').default; }
|
|
8
|
+
function getExpoLocalization() { return require('expo-localization'); }
|
|
9
|
+
|
|
10
|
+
const SDK_CLIENT = 'expo';
|
|
11
|
+
|
|
12
|
+
const TV_SCALE_FACTORS: Record<number, number> = {
|
|
13
|
+
720: 0.67,
|
|
14
|
+
1080: 1,
|
|
15
|
+
1440: 1.5,
|
|
16
|
+
2160: 2,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class ExpoDeviceAdapter implements IDeviceAdapter {
|
|
20
|
+
getDeviceData(): DevicePayload {
|
|
21
|
+
let osName = Platform.OS;
|
|
22
|
+
let osVersion = String(Platform.Version);
|
|
23
|
+
let deviceModel: string | undefined;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const device = getExpoDevice();
|
|
27
|
+
if (device) {
|
|
28
|
+
osName = device.osName ?? osName;
|
|
29
|
+
osVersion = device.osVersion ?? osVersion;
|
|
30
|
+
deviceModel = device.modelName ?? device.modelId;
|
|
31
|
+
}
|
|
32
|
+
} catch { /* expo-device unavailable */ }
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
os_name: osName,
|
|
36
|
+
os_version: osVersion,
|
|
37
|
+
browser_name: '',
|
|
38
|
+
browser_version: '',
|
|
39
|
+
sdk_client: SDK_CLIENT,
|
|
40
|
+
sdk_version: NAMI_SDK_PACKAGE_VERSION,
|
|
41
|
+
language: this.getLanguage(),
|
|
42
|
+
...(deviceModel ? { device_model: deviceModel } : {}),
|
|
43
|
+
extended_platform: 'expo',
|
|
44
|
+
extended_platform_version: this.getExpoVersion(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private getExpoVersion(): string {
|
|
49
|
+
try {
|
|
50
|
+
const constants = getExpoConstants();
|
|
51
|
+
return constants?.expoConfig?.sdkVersion ?? '';
|
|
52
|
+
} catch {
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getDeviceFormFactor(): TDevice {
|
|
58
|
+
try {
|
|
59
|
+
const device = getExpoDevice();
|
|
60
|
+
const deviceType = device?.deviceType;
|
|
61
|
+
const DeviceType = device?.DeviceType;
|
|
62
|
+
|
|
63
|
+
if (DeviceType && deviceType === DeviceType.TV) return 'television';
|
|
64
|
+
if (DeviceType && deviceType === DeviceType.TABLET) return 'tablet';
|
|
65
|
+
} catch {
|
|
66
|
+
// expo-device may not be available (e.g., Expo Go without native modules)
|
|
67
|
+
}
|
|
68
|
+
return 'phone';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getDeviceScaleFactor(formFactor?: string): number {
|
|
72
|
+
const ff = formFactor ?? this.getDeviceFormFactor();
|
|
73
|
+
if (ff !== 'television') return 1;
|
|
74
|
+
|
|
75
|
+
const { height } = Dimensions.get('window');
|
|
76
|
+
const closest = Object.keys(TV_SCALE_FACTORS)
|
|
77
|
+
.map(Number)
|
|
78
|
+
.reduce((prev, curr) =>
|
|
79
|
+
Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev
|
|
80
|
+
);
|
|
81
|
+
return TV_SCALE_FACTORS[closest] ?? 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
generateUUID(): string {
|
|
85
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
86
|
+
const r = (Math.random() * 16) | 0;
|
|
87
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
88
|
+
return v.toString(16);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getScreenInfo(): ScreenInfo {
|
|
93
|
+
const { width, height, scale } = Dimensions.get('window');
|
|
94
|
+
return { width, height, scale };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getLanguage(): string {
|
|
98
|
+
try {
|
|
99
|
+
const { getLocales } = getExpoLocalization();
|
|
100
|
+
const locales = getLocales();
|
|
101
|
+
return locales?.[0]?.languageCode ?? 'en';
|
|
102
|
+
} catch {
|
|
103
|
+
return 'en';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IPurchaseAdapter, PurchaseContext, PurchaseResult } from '@namiml/sdk-core';
|
|
2
|
+
import type { NamiProductDetails } from '@namiml/sdk-core';
|
|
3
|
+
|
|
4
|
+
type ExpoNamiIapModule = {
|
|
5
|
+
initConnection: () => Promise<void>;
|
|
6
|
+
getProducts: (skuIds: string[]) => Promise<NamiProductDetails[]>;
|
|
7
|
+
purchase: (params: {
|
|
8
|
+
skuId: string;
|
|
9
|
+
offerId?: string;
|
|
10
|
+
appAccountToken?: string;
|
|
11
|
+
}) => Promise<{
|
|
12
|
+
transactionId: string;
|
|
13
|
+
receipt?: string;
|
|
14
|
+
skuId: string;
|
|
15
|
+
}>;
|
|
16
|
+
restorePurchases: () => Promise<Array<{
|
|
17
|
+
transactionId: string;
|
|
18
|
+
receipt?: string;
|
|
19
|
+
skuId: string;
|
|
20
|
+
}>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function getExpoNamiIap(): ExpoNamiIapModule {
|
|
24
|
+
return require('@namiml/expo-nami-iap') as ExpoNamiIapModule;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type PurchaseContextWithAppAccountToken = PurchaseContext & {
|
|
28
|
+
appAccountToken?: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function getPurchaseErrorMessage(error: unknown): string {
|
|
32
|
+
if (error instanceof Error && error.message) {
|
|
33
|
+
return error.message;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof error === 'object' && error && 'message' in error) {
|
|
37
|
+
const message = (error as { message?: unknown }).message;
|
|
38
|
+
if (typeof message === 'string' && message.length > 0) {
|
|
39
|
+
return message;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return 'Purchase failed';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ExpoPurchaseAdapter implements IPurchaseAdapter {
|
|
47
|
+
async getProducts(skuIds: string[]): Promise<NamiProductDetails[]> {
|
|
48
|
+
const { initConnection, getProducts } = getExpoNamiIap();
|
|
49
|
+
await initConnection();
|
|
50
|
+
return getProducts(skuIds);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async purchase(skuId: string, context?: PurchaseContext): Promise<PurchaseResult> {
|
|
54
|
+
const { initConnection, purchase } = getExpoNamiIap();
|
|
55
|
+
await initConnection();
|
|
56
|
+
try {
|
|
57
|
+
const tx = await purchase({
|
|
58
|
+
skuId,
|
|
59
|
+
offerId: context?.offerId,
|
|
60
|
+
appAccountToken: (context as PurchaseContextWithAppAccountToken | undefined)?.appAccountToken,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
transactionId: tx.transactionId,
|
|
65
|
+
receipt: tx.receipt,
|
|
66
|
+
skuId: tx.skuId,
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return { success: false, skuId, message: getPurchaseErrorMessage(error) };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async restorePurchases(): Promise<PurchaseResult[]> {
|
|
74
|
+
const { initConnection, restorePurchases } = getExpoNamiIap();
|
|
75
|
+
await initConnection();
|
|
76
|
+
const txs = await restorePurchases();
|
|
77
|
+
return txs.map((t) => ({ success: true, transactionId: t.transactionId, receipt: t.receipt, skuId: t.skuId }));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { IStorageAdapter } from '@namiml/sdk-core';
|
|
2
|
+
|
|
3
|
+
type SQLiteRow = { value?: string; key?: string };
|
|
4
|
+
type SQLiteDatabase = {
|
|
5
|
+
runSync(query: string, params?: unknown[]): void;
|
|
6
|
+
getFirstSync(query: string, params?: unknown[]): SQLiteRow | null;
|
|
7
|
+
getAllSync(query: string, params?: unknown[]): SQLiteRow[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class ExpoStorageAdapter implements IStorageAdapter {
|
|
11
|
+
private db: SQLiteDatabase | null = null;
|
|
12
|
+
private memoryFallback: Map<string, string> | null = null;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
try {
|
|
16
|
+
const { openDatabaseSync } = require('expo-sqlite');
|
|
17
|
+
const database = openDatabaseSync('nami-sdk-storage') as SQLiteDatabase;
|
|
18
|
+
database.runSync(
|
|
19
|
+
'CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)'
|
|
20
|
+
);
|
|
21
|
+
this.db = database;
|
|
22
|
+
} catch {
|
|
23
|
+
this.memoryFallback = new Map();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getItem(key: string): string | null {
|
|
28
|
+
if (this.memoryFallback) return this.memoryFallback.get(key) ?? null;
|
|
29
|
+
const database = this.db;
|
|
30
|
+
if (!database) return null;
|
|
31
|
+
try {
|
|
32
|
+
const row = database.getFirstSync('SELECT value FROM kv WHERE key = ?', [key]);
|
|
33
|
+
return row?.value ?? null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setItem(key: string, value: string): void {
|
|
40
|
+
if (this.memoryFallback) {
|
|
41
|
+
this.memoryFallback.set(key, value);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const database = this.db;
|
|
45
|
+
if (!database) return;
|
|
46
|
+
try {
|
|
47
|
+
database.runSync('INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)', [key, value]);
|
|
48
|
+
} catch {
|
|
49
|
+
/* silent */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
removeItem(key: string): void {
|
|
54
|
+
if (this.memoryFallback) {
|
|
55
|
+
this.memoryFallback.delete(key);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const database = this.db;
|
|
59
|
+
if (!database) return;
|
|
60
|
+
try {
|
|
61
|
+
database.runSync('DELETE FROM kv WHERE key = ?', [key]);
|
|
62
|
+
} catch {
|
|
63
|
+
/* silent */
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
clear(): void {
|
|
68
|
+
if (this.memoryFallback) {
|
|
69
|
+
this.memoryFallback.clear();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const database = this.db;
|
|
73
|
+
if (!database) return;
|
|
74
|
+
try {
|
|
75
|
+
database.runSync('DELETE FROM kv');
|
|
76
|
+
} catch {
|
|
77
|
+
/* silent */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getAllKeys(): string[] {
|
|
82
|
+
if (this.memoryFallback) return [...this.memoryFallback.keys()];
|
|
83
|
+
const database = this.db;
|
|
84
|
+
if (!database) return [];
|
|
85
|
+
try {
|
|
86
|
+
const rows = database.getAllSync('SELECT key FROM kv');
|
|
87
|
+
return rows.map((row) => row.key).filter((key): key is string => typeof key === 'string');
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { IUIAdapter, PaywallHandle, FlowNavigationOptions } from '@namiml/sdk-core';
|
|
2
|
+
import type { IPaywall } from '@namiml/sdk-core';
|
|
3
|
+
import type { NamiPaywallLaunchContext } from '@namiml/sdk-core';
|
|
4
|
+
|
|
5
|
+
type PaywallListener = (paywall: IPaywall, context: NamiPaywallLaunchContext) => void;
|
|
6
|
+
type ReRenderListener = () => void;
|
|
7
|
+
type FlowNavListener = (paywall: IPaywall, options: FlowNavigationOptions) => void;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Expo UI adapter.
|
|
11
|
+
* Uses a listener pattern so the Expo PaywallView component can register
|
|
12
|
+
* itself to receive create/reRender/flowNav events from the core SDK.
|
|
13
|
+
*/
|
|
14
|
+
export class ExpoUIAdapter implements IUIAdapter {
|
|
15
|
+
private paywallListener: PaywallListener | null = null;
|
|
16
|
+
private reRenderListener: ReRenderListener | null = null;
|
|
17
|
+
private flowNavListener: FlowNavListener | null = null;
|
|
18
|
+
|
|
19
|
+
onPaywallRequested(listener: PaywallListener): () => void {
|
|
20
|
+
this.paywallListener = listener;
|
|
21
|
+
return () => { this.paywallListener = null; };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onReRenderRequested(listener: ReRenderListener): () => void {
|
|
25
|
+
this.reRenderListener = listener;
|
|
26
|
+
return () => { this.reRenderListener = null; };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
onFlowNavigationRequested(listener: FlowNavListener): () => void {
|
|
30
|
+
this.flowNavListener = listener;
|
|
31
|
+
return () => { this.flowNavListener = null; };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
createPaywall(
|
|
35
|
+
type: string | undefined,
|
|
36
|
+
value: string,
|
|
37
|
+
context: NamiPaywallLaunchContext
|
|
38
|
+
): PaywallHandle {
|
|
39
|
+
// In Expo, we don't create a DOM element. Instead, we notify the
|
|
40
|
+
// registered PaywallView component to present the paywall.
|
|
41
|
+
// The PaywallView resolves the paywall data using the same core logic.
|
|
42
|
+
if (this.paywallListener) {
|
|
43
|
+
// Build minimal paywall info for navigation
|
|
44
|
+
const paywallInfo = { type, value, context };
|
|
45
|
+
this.paywallListener(paywallInfo as any, context);
|
|
46
|
+
}
|
|
47
|
+
return { type, value, context };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
reRenderPaywall(): void {
|
|
51
|
+
this.reRenderListener?.();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
flowNavigateToScreen(paywall: IPaywall, options: FlowNavigationOptions): void {
|
|
55
|
+
this.flowNavListener?.(paywall, options);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { logger, registerPlatformAdapters, registerPurchaseAdapter } from '@namiml/sdk-core';
|
|
2
|
+
import { ExpoStorageAdapter } from './expo-storage.adapter';
|
|
3
|
+
import { ExpoDeviceAdapter } from './expo-device.adapter';
|
|
4
|
+
import { ExpoUIAdapter } from './expo-ui.adapter';
|
|
5
|
+
import { ExpoPurchaseAdapter } from './expo-purchase.adapter';
|
|
6
|
+
|
|
7
|
+
export const expoUIAdapter = new ExpoUIAdapter();
|
|
8
|
+
let adaptersRegistered = false;
|
|
9
|
+
|
|
10
|
+
export function registerExpoAdapters(): void {
|
|
11
|
+
if (adaptersRegistered) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
registerPlatformAdapters({
|
|
17
|
+
storage: new ExpoStorageAdapter(),
|
|
18
|
+
device: new ExpoDeviceAdapter(),
|
|
19
|
+
ui: expoUIAdapter,
|
|
20
|
+
});
|
|
21
|
+
registerPurchaseAdapter(new ExpoPurchaseAdapter());
|
|
22
|
+
adaptersRegistered = true;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.warn('[NamiExpo] Failed to register Expo adapters.', error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
registerExpoAdapters();
|
|
29
|
+
|
|
30
|
+
export { ExpoStorageAdapter } from './expo-storage.adapter';
|
|
31
|
+
export { ExpoDeviceAdapter } from './expo-device.adapter';
|
|
32
|
+
export { ExpoUIAdapter } from './expo-ui.adapter';
|
|
33
|
+
export { ExpoPurchaseAdapter } from './expo-purchase.adapter';
|