apps-sdk 1.1.59 → 1.1.61
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/SDK_GUIDELINES.md +386 -0
- package/index.js +0 -2
- package/package.json +1 -1
- package/src/libraries/Adapty.js +15 -0
- package/types/index.d.ts +1 -17
- package/PLAN_SDK_UNIFICADO.md +0 -319
- package/src/components/AdaptyOnboarding.js +0 -186
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# SDK Guidelines para Agentes IA
|
|
2
|
+
|
|
3
|
+
## Arquitectura
|
|
4
|
+
|
|
5
|
+
SDK monolítico para React Native/Expo que centraliza funcionalidades core de aplicaciones móviles. No es multi-plataforma aún (planificado). Estructura singleton con módulos especializados.
|
|
6
|
+
|
|
7
|
+
## Punto de Entrada
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import SDK from 'apps-sdk';
|
|
11
|
+
await SDK.session.init();
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Inicialización obligatoria**: `SDK.session.init()` debe ejecutarse al inicio de la app. Configura:
|
|
15
|
+
- Estructura de sesión con datos de device/app
|
|
16
|
+
- UserID persistente
|
|
17
|
+
- Endpoints desde backend
|
|
18
|
+
- Configuración de tracking y eventos
|
|
19
|
+
- Validación de suscripción
|
|
20
|
+
|
|
21
|
+
## Módulos Core
|
|
22
|
+
|
|
23
|
+
### Session (`SDK.session`)
|
|
24
|
+
Gestión de sesión, datos de usuario y configuración global.
|
|
25
|
+
|
|
26
|
+
**Responsabilidades**:
|
|
27
|
+
- Generar y mantener sessionID
|
|
28
|
+
- Almacenar datos de device (modelo, OS, versión)
|
|
29
|
+
- Gestionar userID persistente
|
|
30
|
+
- Detectar first_open
|
|
31
|
+
- Estado de suscripción
|
|
32
|
+
- Configuración de idioma/región
|
|
33
|
+
|
|
34
|
+
**Flujo típico**:
|
|
35
|
+
```javascript
|
|
36
|
+
await SDK.session.init();
|
|
37
|
+
const userID = SDK.session.getUserID();
|
|
38
|
+
const isSubscribed = SDK.session.getIsSubscribed();
|
|
39
|
+
const deviceLang = SDK.session.getDeviceLanguage();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**sessionData estructura**:
|
|
43
|
+
```javascript
|
|
44
|
+
{
|
|
45
|
+
user_id: string,
|
|
46
|
+
isFirstOpen: boolean,
|
|
47
|
+
app: { shortVersion, package, languageCode, regionCode, buildVersionNumber },
|
|
48
|
+
device: { name, systemName, systemVersion, model },
|
|
49
|
+
adjust: { attribution_id, idfa, googleAdid, attribution },
|
|
50
|
+
lang: string,
|
|
51
|
+
package: string
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Storage (`SDK.storage`)
|
|
56
|
+
Persistencia local con AsyncStorage + FileSystem de Expo.
|
|
57
|
+
|
|
58
|
+
**Patrones de uso**:
|
|
59
|
+
```javascript
|
|
60
|
+
// Datos JSON simples
|
|
61
|
+
await SDK.storage.storeData(key, value);
|
|
62
|
+
const data = await SDK.storage.getData(key);
|
|
63
|
+
await SDK.storage.removeData(key);
|
|
64
|
+
|
|
65
|
+
// Imágenes en galería
|
|
66
|
+
await SDK.storage.handleDownloadImageToGallery(imageUri);
|
|
67
|
+
|
|
68
|
+
// Sistema de archivos (creations)
|
|
69
|
+
await SDK.storage.handleDownloadImageToCreations(base64, fileName, metadata);
|
|
70
|
+
const creations = await SDK.storage.getCreations();
|
|
71
|
+
await SDK.storage.deleteCreation(dirName);
|
|
72
|
+
|
|
73
|
+
// Compresión automática
|
|
74
|
+
const compressed = await SDK.storage.compressImage(imageUri, maxSizeMB);
|
|
75
|
+
|
|
76
|
+
// Compartir archivos
|
|
77
|
+
await SDK.storage.handleShareFile(fileUri);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Directorios**:
|
|
81
|
+
- `${FileSystem.documentDirectory}/creations` - Almacenamiento persistente
|
|
82
|
+
- `${FileSystem.documentDirectory}/tmp` - Temporal (se limpia con `deleteTempFiles()`)
|
|
83
|
+
|
|
84
|
+
**Permisos**: Gestiona permisos de galería automáticamente, solicitándolos cuando sea necesario.
|
|
85
|
+
|
|
86
|
+
### Networking (`SDK.networking`)
|
|
87
|
+
Comunicación HTTP con backend. Todas las peticiones incluyen `sessionData` automáticamente.
|
|
88
|
+
|
|
89
|
+
**Request unificado**:
|
|
90
|
+
```javascript
|
|
91
|
+
const response = await SDK.networking.request(url, data);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Sistema de eventos**:
|
|
95
|
+
```javascript
|
|
96
|
+
await SDK.networking.sendEvent(eventType, eventKeyword, eventData);
|
|
97
|
+
// eventType: 'action', 'screen', 'other'
|
|
98
|
+
// Se envía a tracking (Adjust) automáticamente
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Eventos especiales**: Primera ocurrencia de eventos se trackea con prefijo `first_` (ej: `first_paywall_shown`).
|
|
102
|
+
|
|
103
|
+
**Pending Events**: Cola para eventos que no pueden enviarse de inmediato:
|
|
104
|
+
```javascript
|
|
105
|
+
SDK.networking.addPendingEvent({ eventType, eventKeyword, eventData });
|
|
106
|
+
SDK.networking.sendPendingEvents();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Configuración dinámica**: `executeInit()` carga desde backend:
|
|
110
|
+
- Endpoints (`config.ENDPOINTS`)
|
|
111
|
+
- Eventos de tracking (`config.EVENTS`)
|
|
112
|
+
- Configuración de paywall (`config.PAYWALL_DATA`)
|
|
113
|
+
- Forced update check
|
|
114
|
+
- Quick actions
|
|
115
|
+
- Compresión de imágenes
|
|
116
|
+
|
|
117
|
+
### Utils (`SDK.utils`)
|
|
118
|
+
Funciones auxiliares:
|
|
119
|
+
```javascript
|
|
120
|
+
SDK.utils.isBase64(str);
|
|
121
|
+
SDK.utils.isBase64Image(str);
|
|
122
|
+
SDK.utils.isSpecialEvent(eventKeyword);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Integraciones de Analytics
|
|
126
|
+
|
|
127
|
+
### Adjust (`SDK.adjust`)
|
|
128
|
+
Attribution y tracking de instalación.
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
await SDK.adjust.initialize(adjustToken, sendAttributionToAdapty);
|
|
132
|
+
SDK.adjust.trackEventIfExist(eventKeyword, eventValue);
|
|
133
|
+
await SDK.adjust.getAttribution();
|
|
134
|
+
await SDK.adjust.getIdfa(); // iOS
|
|
135
|
+
await SDK.adjust.getGoogleAdId(); // Android
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Integración con Session**: Attribution data se almacena en `sessionData.adjust`.
|
|
139
|
+
|
|
140
|
+
### MixPanel (`SDK.mixpanel`)
|
|
141
|
+
Analytics de comportamiento.
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
await SDK.mixpanel.initialize(token, trackAutomaticEvents, useNative, devMode);
|
|
145
|
+
await SDK.mixpanel.trackEvent(event, properties);
|
|
146
|
+
await SDK.mixpanel.identifyUser(userID);
|
|
147
|
+
await SDK.mixpanel.superProperties({ plan_type: 'premium' });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Super Properties**: Propiedades globales que se envían con todos los eventos.
|
|
151
|
+
|
|
152
|
+
### Facebook (`SDK.facebook`)
|
|
153
|
+
Facebook Analytics.
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
await SDK.facebook.initialize(appId, clientToken, debugMode, devMode);
|
|
157
|
+
await SDK.facebook.trackEvent(eventName, properties);
|
|
158
|
+
await SDK.facebook.trackPurchase(amount, currency);
|
|
159
|
+
await SDK.facebook.trackStartTrial();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Sistema de Paywall
|
|
163
|
+
|
|
164
|
+
### Adapty (`SDK.adapty`)
|
|
165
|
+
Gestión de suscripciones y paywalls.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
await SDK.adapty.initialize(apiKey);
|
|
169
|
+
await SDK.adapty.showPaywall(placementID, lang, eventHandlers);
|
|
170
|
+
const profile = await SDK.adapty.getProfile();
|
|
171
|
+
await SDK.adapty.updateAdjustAttributionData(attribution);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Event Handlers**:
|
|
175
|
+
```javascript
|
|
176
|
+
{
|
|
177
|
+
onPurchaseCompleted: (result, product) => {},
|
|
178
|
+
onPurchaseFailed: (error) => {},
|
|
179
|
+
onRestoreCompleted: (profile) => {},
|
|
180
|
+
onCloseButtonPress: () => {}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Onboarding**:
|
|
185
|
+
```javascript
|
|
186
|
+
await SDK.adapty.showOnboarding(placementID, lang, eventHandlers);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### PayWall Component (`SDK.paywall`)
|
|
190
|
+
Componente React para paywalls custom.
|
|
191
|
+
|
|
192
|
+
```jsx
|
|
193
|
+
<SDK.paywall
|
|
194
|
+
visible={true}
|
|
195
|
+
onClose={() => {}}
|
|
196
|
+
type="subscription"
|
|
197
|
+
keyword="premium_monthly"
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### PayWallLogic (`SDK.paywallLogic`)
|
|
202
|
+
Lógica de compra con react-native-iap (legacy, usar Adapty).
|
|
203
|
+
|
|
204
|
+
## Funcionalidades Específicas
|
|
205
|
+
|
|
206
|
+
### Notifications (`SDK.notifications`)
|
|
207
|
+
Push notifications con Expo Notifications.
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
await SDK.initializePushNotifications(); // Retorna expo token
|
|
211
|
+
const status = await SDK.getPushNotificationsStatus();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Token se envía automáticamente** al backend en `initializePushNotifications()`.
|
|
215
|
+
|
|
216
|
+
### Rating (`SDK.rating`)
|
|
217
|
+
Sistema de reviews.
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
await SDK.rating.showRatingDialog(force);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Voice (`SDK.voice`)
|
|
224
|
+
Speech-to-text y text-to-speech.
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
// Speech-to-text
|
|
228
|
+
await SDK.voice.startRecognizing(
|
|
229
|
+
onSpeechStart,
|
|
230
|
+
onSpeechRecognized,
|
|
231
|
+
onSpeechResults,
|
|
232
|
+
onInactivityTimeout,
|
|
233
|
+
inactivitySeconds
|
|
234
|
+
);
|
|
235
|
+
await SDK.voice.stopRecognizing();
|
|
236
|
+
|
|
237
|
+
// Text-to-speech
|
|
238
|
+
SDK.voice.speak(message, language, voice, onStart, onDone, onError);
|
|
239
|
+
await SDK.voice.stopSpeaking();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### TrackingTransparency (`SDK.tracking`)
|
|
243
|
+
ATT para iOS.
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
const status = await SDK.tracking.requestTrackingTransparencyPermission();
|
|
247
|
+
const idfa = await SDK.tracking.getAdvertisingIdentifier();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Almacenamiento automático**: El estado se guarda en `TRACKING_PERMISSION` y actualiza `config.TRACKING_ACTIVE`.
|
|
251
|
+
|
|
252
|
+
### HomeActions (`SDK.homeActions`)
|
|
253
|
+
Quick Actions (iOS/Android shortcuts).
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
await SDK.homeActions.setItems(quickActionsArray);
|
|
257
|
+
await SDK.homeActions.itemCallback(callbackFunction);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Configuración Global
|
|
261
|
+
|
|
262
|
+
### config.js
|
|
263
|
+
Variables globales del SDK:
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
config.DEBUG_MODE = true/false;
|
|
267
|
+
config.TRACKING_ACTIVE = true/false;
|
|
268
|
+
config.FORCED_UPDATE = true/false;
|
|
269
|
+
config.ENDPOINTS = { CONFIG, EVENTS_PUSH, SUB_STATUS, ... };
|
|
270
|
+
config.EVENTS = { eventKeyword: eventToken };
|
|
271
|
+
config.EVENTS_MIXPANEL = { eventKeyword: eventName };
|
|
272
|
+
config.PAYWALL_DATA = { actions, scenes, others, products };
|
|
273
|
+
config.CONFIG_EXTRA = {}; // Configuración custom desde backend
|
|
274
|
+
config.QUICK_ACTIONS = []; // Quick actions desde backend
|
|
275
|
+
config.IMAGE_COMPRESSION = { ACTIVE, COMPRESSION, WIDTH };
|
|
276
|
+
config.EVENT_TYPES = { ACTION: 'action', SCREEN: 'screen', OTHER: 'other' };
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Principios de Diseño
|
|
280
|
+
|
|
281
|
+
1. **Singleton Pattern**: Todos los módulos son instancias singleton exportadas directamente
|
|
282
|
+
2. **Auto-inicialización**: Módulos se inicializan en importación cuando sea posible
|
|
283
|
+
3. **sessionData global**: Todas las peticiones incluyen sessionData automáticamente
|
|
284
|
+
4. **Persistencia transparente**: Storage abstrae AsyncStorage para uso sencillo
|
|
285
|
+
5. **Error handling silencioso**: Logs en consola, no crashes
|
|
286
|
+
6. **Permission management**: Gestión automática de permisos con fallback a settings
|
|
287
|
+
|
|
288
|
+
## Convenciones
|
|
289
|
+
|
|
290
|
+
- **Async/await**: Todas las operaciones I/O usan async/await
|
|
291
|
+
- **Console logs condicionales**: `config.DEBUG_MODE && console.debug(...)`
|
|
292
|
+
- **Naming**: camelCase para métodos, UPPER_CASE para constantes globales
|
|
293
|
+
- **Return values**: null en caso de error, throw solo cuando sea crítico
|
|
294
|
+
- **Storage keys**: Snake_case en mayúsculas (ej: `TRACKING_PERMISSION`)
|
|
295
|
+
- **Event naming**: snake_case en minúsculas (ej: `first_open`, `paywall_shown`)
|
|
296
|
+
|
|
297
|
+
## Dependencias Críticas
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"@react-native-async-storage/async-storage": "Persistencia",
|
|
302
|
+
"expo-file-system": "Sistema de archivos",
|
|
303
|
+
"expo-media-library": "Galería",
|
|
304
|
+
"expo-notifications": "Push notifications",
|
|
305
|
+
"expo-constants": "App metadata",
|
|
306
|
+
"expo-device": "Device info",
|
|
307
|
+
"react-native-adapty": "Suscripciones",
|
|
308
|
+
"react-native-adjust": "Attribution",
|
|
309
|
+
"mixpanel-react-native": "Analytics",
|
|
310
|
+
"react-native-fbsdk-next": "Facebook",
|
|
311
|
+
"@react-native-voice/voice": "Speech",
|
|
312
|
+
"expo-tracking-transparency": "ATT iOS",
|
|
313
|
+
"react-native-iap": "IAP legacy",
|
|
314
|
+
"semver": "Version comparison"
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Flujo de Inicialización Típico
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// 1. Inicializar SDK
|
|
322
|
+
await SDK.session.init();
|
|
323
|
+
|
|
324
|
+
// 2. Configurar tracking (si necesario)
|
|
325
|
+
const trackingStatus = await SDK.tracking.requestTrackingTransparencyPermission();
|
|
326
|
+
await SDK.storage.setTrackingPermissionGranted(trackingStatus === 'authorized');
|
|
327
|
+
|
|
328
|
+
// 3. Inicializar analytics
|
|
329
|
+
await SDK.adjust.initialize(ADJUST_TOKEN, true);
|
|
330
|
+
await SDK.mixpanel.initialize(MIXPANEL_TOKEN);
|
|
331
|
+
await SDK.facebook.initialize(FB_APP_ID, FB_CLIENT_TOKEN);
|
|
332
|
+
|
|
333
|
+
// 4. Inicializar monetización
|
|
334
|
+
await SDK.adapty.initialize(ADAPTY_KEY);
|
|
335
|
+
await SDK.adapty.setAdjustIntegrationIdentifier(adjustIntegrationID);
|
|
336
|
+
await SDK.adapty.setMixpanelIntegrationIdentifier(mixpanelDistinctID);
|
|
337
|
+
|
|
338
|
+
// 5. Setup notifications
|
|
339
|
+
const expoToken = await SDK.initializePushNotifications();
|
|
340
|
+
|
|
341
|
+
// 6. Limpiar temporal
|
|
342
|
+
await SDK.storage.deleteTempFiles();
|
|
343
|
+
|
|
344
|
+
// 7. Verificar suscripción cada 24h
|
|
345
|
+
await SDK.session.checkSubscription24h();
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## TypeScript Support
|
|
349
|
+
|
|
350
|
+
Definiciones completas en `types/index.d.ts`. Todos los módulos y métodos están tipados.
|
|
351
|
+
|
|
352
|
+
## Debugging
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
// Console override automático con timestamps
|
|
356
|
+
SDK.sobrescribeConsole(); // Ya ejecutado en constructor
|
|
357
|
+
|
|
358
|
+
// Ver todas las claves de AsyncStorage
|
|
359
|
+
await SDK.storage.printAllKeys();
|
|
360
|
+
|
|
361
|
+
// Limpiar todo AsyncStorage
|
|
362
|
+
await SDK.storage.removeAllKeys();
|
|
363
|
+
|
|
364
|
+
// Verificar endpoints cargados
|
|
365
|
+
const endpoints = await SDK.networking.getEndpoints();
|
|
366
|
+
|
|
367
|
+
// Ver configuración extra del backend
|
|
368
|
+
const config = SDK.networking.getConfigExtra();
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Limitaciones Conocidas
|
|
372
|
+
|
|
373
|
+
- **Solo Expo/React Native**: No funciona en web ni otras plataformas
|
|
374
|
+
- **Backend dependency**: Requiere endpoints configurados para init
|
|
375
|
+
- **Singleton limitations**: No soporta múltiples instancias
|
|
376
|
+
- **No lazy loading**: Todos los módulos se cargan al importar
|
|
377
|
+
- **AsyncStorage limits**: No usar para datos grandes (>6MB)
|
|
378
|
+
|
|
379
|
+
## Migración a Arquitectura Multi-plataforma (Futuro)
|
|
380
|
+
|
|
381
|
+
Estructura planificada con adaptadores:
|
|
382
|
+
- `@apps-sdk/core`: Lógica compartida
|
|
383
|
+
- `@apps-sdk/expo`: Adaptadores React Native/Expo
|
|
384
|
+
- `@apps-sdk/web`: Adaptadores Web (localStorage, Canvas, etc)
|
|
385
|
+
|
|
386
|
+
Ver `EJEMPLO_USO_SDK.md` para detalles de arquitectura propuesta.
|
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {NotificationsPush, Networking, Storage, Session, Utils, PayWallLogic, Rating, AdJust, TrackingTransparency, Voice, MixPanel, Adapty, HomeActions, Facebook} from "./src/libraries";
|
|
2
2
|
import PayWall from "./src/components/PayWall";
|
|
3
|
-
import AdaptyOnboarding from "./src/components/AdaptyOnboarding";
|
|
4
3
|
|
|
5
4
|
class AppsSDK {
|
|
6
5
|
constructor() {
|
|
@@ -57,7 +56,6 @@ export default {
|
|
|
57
56
|
mixpanel: MixPanel,
|
|
58
57
|
tracking: TrackingTransparency,
|
|
59
58
|
paywall: PayWall,
|
|
60
|
-
adaptyOnboarding: AdaptyOnboarding,
|
|
61
59
|
notifications: NotificationsPush,
|
|
62
60
|
voice: Voice,
|
|
63
61
|
adapty: Adapty,
|
package/package.json
CHANGED
package/src/libraries/Adapty.js
CHANGED
|
@@ -75,11 +75,14 @@ class Adapty {
|
|
|
75
75
|
onCustomAction: eventHandlers.onCustomAction || (() => { }),
|
|
76
76
|
});
|
|
77
77
|
await paywallView.present();
|
|
78
|
+
return paywallView;
|
|
78
79
|
} else {
|
|
79
80
|
console.warn('Error showing paywall: paywall not found for placement', placementID, 'and language', lang);
|
|
81
|
+
return null;
|
|
80
82
|
}
|
|
81
83
|
} catch (error) {
|
|
82
84
|
console.error('Error showing paywall:', error);
|
|
85
|
+
return null;
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -120,6 +123,18 @@ class Adapty {
|
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
126
|
+
async closePaywallFromView(paywallView) {
|
|
127
|
+
try {
|
|
128
|
+
if (paywallView) {
|
|
129
|
+
paywallView.dismiss();
|
|
130
|
+
} else {
|
|
131
|
+
console.warn('Error closing paywall: paywall view is null');
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Error closing paywall:', error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
123
138
|
async showPaywallPreloaded(paywallView, eventHandlers = {}) {
|
|
124
139
|
try {
|
|
125
140
|
if (paywallView) {
|
package/types/index.d.ts
CHANGED
|
@@ -158,22 +158,6 @@ declare module 'apps-sdk' {
|
|
|
158
158
|
|
|
159
159
|
export class PayWall extends Component<PayWallProps, {}> {}
|
|
160
160
|
|
|
161
|
-
export interface AdaptyOnboardingProps {
|
|
162
|
-
visible: boolean;
|
|
163
|
-
placementID: string;
|
|
164
|
-
lang: string;
|
|
165
|
-
onCustom?: (actionId: string, meta: Record<string, any> | undefined, remoteConfig: Record<string, any> | null) => void;
|
|
166
|
-
onClose?: (actionId: string, meta: Record<string, any> | undefined, remoteConfig: Record<string, any> | null) => boolean;
|
|
167
|
-
onError?: (error: Error | any, remoteConfig: Record<string, any> | null) => boolean;
|
|
168
|
-
onRemoteConfigLoaded?: (remoteConfig: Record<string, any>) => void;
|
|
169
|
-
onAnalytics?: (event: any, data: any, remoteConfig: Record<string, any> | null) => void;
|
|
170
|
-
onPaywall?: (actionId: string, meta: Record<string, any> | undefined) => void;
|
|
171
|
-
onStateUpdated?: (action: any, meta: Record<string, any> | undefined, remoteConfig: Record<string, any> | null) => void;
|
|
172
|
-
onFinishedLoading?: (meta: Record<string, any> | undefined, remoteConfig: Record<string, any> | null) => void;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export class AdaptyOnboarding extends Component<AdaptyOnboardingProps, {}> {}
|
|
176
|
-
|
|
177
161
|
export class PayWallLogic {
|
|
178
162
|
initializePayWall(): Promise<void>;
|
|
179
163
|
executePurchase(productID: string): Promise<void>;
|
|
@@ -258,6 +242,7 @@ declare module 'apps-sdk' {
|
|
|
258
242
|
preloadOnboarding(placementID: string, lang: string): Promise<any>;
|
|
259
243
|
closeOnboarding(): Promise<void>;
|
|
260
244
|
showOnboardingPreloaded(onboardingView: any, eventHandlers?: OnboardingEventHandlers): Promise<void>;
|
|
245
|
+
closePaywallFromView(paywallView: any): Promise<void>;
|
|
261
246
|
}
|
|
262
247
|
|
|
263
248
|
export class TrackingTransparency {
|
|
@@ -294,7 +279,6 @@ declare module 'apps-sdk' {
|
|
|
294
279
|
session : Session;
|
|
295
280
|
utils : Utils;
|
|
296
281
|
paywall : PayWall;
|
|
297
|
-
adaptyOnboarding : typeof AdaptyOnboarding;
|
|
298
282
|
rating : Rating;
|
|
299
283
|
adjust : AdJust;
|
|
300
284
|
mixpanel : MixPanel;
|
package/PLAN_SDK_UNIFICADO.md
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
# Plan para Crear SDK Unificado (Expo + Web)
|
|
2
|
-
|
|
3
|
-
## Objetivo
|
|
4
|
-
Crear un SDK único que funcione tanto para aplicaciones Expo/React Native como para aplicaciones web (Next.js/React), maximizando la reutilización de código y manteniendo una API consistente.
|
|
5
|
-
|
|
6
|
-
## Análisis del Estado Actual
|
|
7
|
-
|
|
8
|
-
### Funcionalidades Existentes en SDK Expo
|
|
9
|
-
- ✅ **Networking** - Gestión de APIs y comunicación con backend
|
|
10
|
-
- ✅ **Storage** - Almacenamiento local y gestión de archivos/imágenes
|
|
11
|
-
- ✅ **Session** - Gestión de sesión de usuario y datos del dispositivo
|
|
12
|
-
- ✅ **Analytics** - MixPanel, Adjust, tracking de eventos
|
|
13
|
-
- ✅ **Monetización** - Adapty, PayWall, suscripciones
|
|
14
|
-
- ✅ **Notificaciones Push** - Expo notifications
|
|
15
|
-
- ✅ **Utilidades** - Funciones helper, validaciones
|
|
16
|
-
- ✅ **Voz** - Reconocimiento y síntesis de voz
|
|
17
|
-
- ✅ **Rating** - Sistema de valoraciones
|
|
18
|
-
- ✅ **Quick Actions** - Acciones rápidas del sistema
|
|
19
|
-
- ✅ **Tracking Transparency** - Permisos de tracking iOS
|
|
20
|
-
|
|
21
|
-
### Código Reutilizable para Web
|
|
22
|
-
|
|
23
|
-
#### 🟢 Altamente Reutilizable (80-90%)
|
|
24
|
-
- **Networking.js** - Lógica de APIs, fetch, gestión de eventos
|
|
25
|
-
- **Session.js** - Gestión de sesión (adaptando detección de dispositivo)
|
|
26
|
-
- **Utils.js** - Funciones helper, validaciones base64
|
|
27
|
-
- **PayWallLogic.js** - Lógica de negocio de suscripciones
|
|
28
|
-
- **MixPanel.js** - Analytics (con SDK web de MixPanel)
|
|
29
|
-
- **AdJust.js** - Attribution (con SDK web de Adjust)
|
|
30
|
-
|
|
31
|
-
#### 🟡 Parcialmente Reutilizable (40-60%)
|
|
32
|
-
- **Storage.js** - Lógica de almacenamiento (localStorage/IndexedDB vs AsyncStorage)
|
|
33
|
-
- **Adapty.js** - Monetización (con adaptaciones para web)
|
|
34
|
-
- **Notifications.js** - Web Push API vs Expo notifications
|
|
35
|
-
|
|
36
|
-
#### 🔴 No Reutilizable Directamente
|
|
37
|
-
- **Voice.js** - Requiere Web Speech API
|
|
38
|
-
- **TrackingTransparency.js** - Específico de iOS
|
|
39
|
-
- **QuickActions.js** - Específico de móvil
|
|
40
|
-
- **Rating.js** - Requiere implementación web custom
|
|
41
|
-
|
|
42
|
-
## Arquitectura Propuesta
|
|
43
|
-
|
|
44
|
-
### Estructura del Proyecto
|
|
45
|
-
```
|
|
46
|
-
apps-sdk-unified/
|
|
47
|
-
├── packages/
|
|
48
|
-
│ ├── core/ # Lógica compartida
|
|
49
|
-
│ │ ├── src/
|
|
50
|
-
│ │ │ ├── networking/
|
|
51
|
-
│ │ │ ├── session/
|
|
52
|
-
│ │ │ ├── analytics/
|
|
53
|
-
│ │ │ ├── monetization/
|
|
54
|
-
│ │ │ ├── utils/
|
|
55
|
-
│ │ │ └── types/
|
|
56
|
-
│ │ ├── package.json
|
|
57
|
-
│ │ └── tsconfig.json
|
|
58
|
-
│ ├── expo/ # Adaptadores Expo
|
|
59
|
-
│ │ ├── src/
|
|
60
|
-
│ │ │ ├── adapters/
|
|
61
|
-
│ │ │ ├── components/
|
|
62
|
-
│ │ │ └── index.ts
|
|
63
|
-
│ │ ├── package.json
|
|
64
|
-
│ │ └── tsconfig.json
|
|
65
|
-
│ ├── web/ # Adaptadores Web
|
|
66
|
-
│ │ ├── src/
|
|
67
|
-
│ │ │ ├── adapters/
|
|
68
|
-
│ │ │ ├── components/
|
|
69
|
-
│ │ │ └── index.ts
|
|
70
|
-
│ │ ├── package.json
|
|
71
|
-
│ │ └── tsconfig.json
|
|
72
|
-
│ └── shared/ # Tipos y utilidades compartidas
|
|
73
|
-
│ ├── src/
|
|
74
|
-
│ │ ├── types/
|
|
75
|
-
│ │ └── constants/
|
|
76
|
-
│ ├── package.json
|
|
77
|
-
│ └── tsconfig.json
|
|
78
|
-
├── tools/
|
|
79
|
-
│ ├── build/
|
|
80
|
-
│ └── scripts/
|
|
81
|
-
├── docs/
|
|
82
|
-
├── examples/
|
|
83
|
-
│ ├── expo-example/
|
|
84
|
-
│ └── web-example/
|
|
85
|
-
├── package.json
|
|
86
|
-
├── lerna.json
|
|
87
|
-
└── tsconfig.json
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Plan de Implementación
|
|
91
|
-
|
|
92
|
-
### Fase 1: Preparación y Core (Semanas 1-2)
|
|
93
|
-
|
|
94
|
-
#### 1.1 Configuración del Monorepo
|
|
95
|
-
- [ ] Configurar Lerna/Nx para gestión de monorepo
|
|
96
|
-
- [ ] Configurar TypeScript workspace
|
|
97
|
-
- [ ] Configurar ESLint y Prettier compartidos
|
|
98
|
-
- [ ] Configurar scripts de build y testing
|
|
99
|
-
|
|
100
|
-
#### 1.2 Extracción del Core
|
|
101
|
-
- [ ] Crear package `@apps-sdk/core`
|
|
102
|
-
- [ ] Migrar lógica de negocio compartida:
|
|
103
|
-
- [ ] Networking (sin dependencias de React Native)
|
|
104
|
-
- [ ] Session management (abstracto)
|
|
105
|
-
- [ ] Utils y validaciones
|
|
106
|
-
- [ ] Constantes y configuración
|
|
107
|
-
- [ ] Definir interfaces y tipos compartidos
|
|
108
|
-
- [ ] Crear sistema de adaptadores (Strategy Pattern)
|
|
109
|
-
|
|
110
|
-
#### 1.3 Definición de Interfaces
|
|
111
|
-
```typescript
|
|
112
|
-
// Ejemplo de interfaz para adaptadores
|
|
113
|
-
interface StorageAdapter {
|
|
114
|
-
setItem(key: string, value: string): Promise<void>;
|
|
115
|
-
getItem(key: string): Promise<string | null>;
|
|
116
|
-
removeItem(key: string): Promise<void>;
|
|
117
|
-
clear(): Promise<void>;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface DeviceAdapter {
|
|
121
|
-
getDeviceInfo(): DeviceInfo;
|
|
122
|
-
getLanguage(): string;
|
|
123
|
-
getPlatform(): 'ios' | 'android' | 'web';
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Fase 2: Adaptadores Expo (Semanas 3-4)
|
|
128
|
-
|
|
129
|
-
#### 2.1 Package Expo
|
|
130
|
-
- [ ] Crear `@apps-sdk/expo`
|
|
131
|
-
- [ ] Implementar adaptadores específicos de Expo:
|
|
132
|
-
- [ ] StorageAdapter (AsyncStorage)
|
|
133
|
-
- [ ] DeviceAdapter (expo-device, expo-constants)
|
|
134
|
-
- [ ] NotificationAdapter (expo-notifications)
|
|
135
|
-
- [ ] VoiceAdapter (@react-native-voice/voice)
|
|
136
|
-
- [ ] FileSystemAdapter (expo-file-system)
|
|
137
|
-
|
|
138
|
-
#### 2.2 Componentes Expo
|
|
139
|
-
- [ ] Migrar PayWall component
|
|
140
|
-
- [ ] Adaptar para nueva arquitectura
|
|
141
|
-
- [ ] Mantener compatibilidad con API actual
|
|
142
|
-
|
|
143
|
-
#### 2.3 Testing Expo
|
|
144
|
-
- [ ] Configurar Jest para React Native
|
|
145
|
-
- [ ] Tests unitarios para adaptadores
|
|
146
|
-
- [ ] Tests de integración
|
|
147
|
-
|
|
148
|
-
### Fase 3: Adaptadores Web (Semanas 5-6)
|
|
149
|
-
|
|
150
|
-
#### 3.1 Package Web
|
|
151
|
-
- [ ] Crear `@apps-sdk/web`
|
|
152
|
-
- [ ] Implementar adaptadores web:
|
|
153
|
-
- [ ] StorageAdapter (localStorage/sessionStorage/IndexedDB)
|
|
154
|
-
- [ ] DeviceAdapter (navigator APIs)
|
|
155
|
-
- [ ] NotificationAdapter (Web Push API)
|
|
156
|
-
- [ ] VoiceAdapter (Web Speech API)
|
|
157
|
-
- [ ] FileSystemAdapter (File API)
|
|
158
|
-
|
|
159
|
-
#### 3.2 Componentes Web
|
|
160
|
-
- [ ] Crear PayWall component para React/Next.js
|
|
161
|
-
- [ ] Implementar rating system para web
|
|
162
|
-
- [ ] Crear equivalentes web de funcionalidades móviles
|
|
163
|
-
|
|
164
|
-
#### 3.3 Librerías Web Específicas
|
|
165
|
-
```json
|
|
166
|
-
{
|
|
167
|
-
"dependencies": {
|
|
168
|
-
"mixpanel-browser": "^2.47.0",
|
|
169
|
-
"@adjust/web-sdk": "^5.7.0",
|
|
170
|
-
"idb": "^7.1.1",
|
|
171
|
-
"js-cookie": "^3.0.5"
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Fase 4: Analytics y Monetización (Semanas 7-8)
|
|
177
|
-
|
|
178
|
-
#### 4.1 Analytics Unificados
|
|
179
|
-
- [ ] Adaptar MixPanel para ambas plataformas
|
|
180
|
-
- [ ] Adaptar Adjust para web
|
|
181
|
-
- [ ] Crear sistema de eventos unificado
|
|
182
|
-
- [ ] Implementar queue de eventos offline
|
|
183
|
-
|
|
184
|
-
#### 4.2 Monetización
|
|
185
|
-
- [ ] Investigar Adapty Web SDK
|
|
186
|
-
- [ ] Implementar alternativas web (Stripe, PayPal)
|
|
187
|
-
- [ ] Crear interfaz unificada de pagos
|
|
188
|
-
- [ ] Adaptar lógica de suscripciones
|
|
189
|
-
|
|
190
|
-
### Fase 5: Testing y Documentación (Semanas 9-10)
|
|
191
|
-
|
|
192
|
-
#### 5.1 Testing Integral
|
|
193
|
-
- [ ] Tests unitarios para todos los packages
|
|
194
|
-
- [ ] Tests de integración cross-platform
|
|
195
|
-
- [ ] Tests E2E con ejemplos
|
|
196
|
-
- [ ] Performance testing
|
|
197
|
-
|
|
198
|
-
#### 5.2 Documentación
|
|
199
|
-
- [ ] API documentation
|
|
200
|
-
- [ ] Migration guide desde SDK actual
|
|
201
|
-
- [ ] Ejemplos de uso
|
|
202
|
-
- [ ] Best practices
|
|
203
|
-
|
|
204
|
-
#### 5.3 Ejemplos
|
|
205
|
-
- [ ] App Expo de ejemplo
|
|
206
|
-
- [ ] App Next.js de ejemplo
|
|
207
|
-
- [ ] Casos de uso comunes
|
|
208
|
-
|
|
209
|
-
### Fase 6: Migración y Deploy (Semanas 11-12)
|
|
210
|
-
|
|
211
|
-
#### 6.1 Migración Gradual
|
|
212
|
-
- [ ] Crear herramientas de migración
|
|
213
|
-
- [ ] Backward compatibility layer
|
|
214
|
-
- [ ] Deprecation warnings
|
|
215
|
-
- [ ] Migration scripts
|
|
216
|
-
|
|
217
|
-
#### 6.2 CI/CD
|
|
218
|
-
- [ ] Configurar GitHub Actions/GitLab CI
|
|
219
|
-
- [ ] Automated testing
|
|
220
|
-
- [ ] Automated publishing a npm
|
|
221
|
-
- [ ] Semantic versioning
|
|
222
|
-
|
|
223
|
-
#### 6.3 Release
|
|
224
|
-
- [ ] Alpha release para testing interno
|
|
225
|
-
- [ ] Beta release para early adopters
|
|
226
|
-
- [ ] Stable release
|
|
227
|
-
- [ ] Post-release monitoring
|
|
228
|
-
|
|
229
|
-
## Consideraciones Técnicas
|
|
230
|
-
|
|
231
|
-
### Gestión de Dependencias
|
|
232
|
-
```json
|
|
233
|
-
{
|
|
234
|
-
"peerDependencies": {
|
|
235
|
-
"react": ">=16.8.0",
|
|
236
|
-
"react-native": ">=0.60.0" // Solo para package expo
|
|
237
|
-
},
|
|
238
|
-
"optionalDependencies": {
|
|
239
|
-
"expo": ">=45.0.0" // Solo si se usa Expo
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Tree Shaking y Bundle Size
|
|
245
|
-
- Configurar builds optimizados para cada plataforma
|
|
246
|
-
- Lazy loading de módulos pesados
|
|
247
|
-
- Conditional imports basados en plataforma
|
|
248
|
-
|
|
249
|
-
### Versionado
|
|
250
|
-
- Semantic versioning para todos los packages
|
|
251
|
-
- Synchronized releases
|
|
252
|
-
- Changelog automatizado
|
|
253
|
-
|
|
254
|
-
## Riesgos y Mitigaciones
|
|
255
|
-
|
|
256
|
-
### Riesgos Identificados
|
|
257
|
-
1. **Complejidad de mantenimiento** - Mitigar con buena arquitectura y testing
|
|
258
|
-
2. **Bundle size** - Mitigar con tree shaking y lazy loading
|
|
259
|
-
3. **Breaking changes** - Mitigar con versionado semántico y deprecation warnings
|
|
260
|
-
4. **Platform-specific bugs** - Mitigar with comprehensive testing
|
|
261
|
-
|
|
262
|
-
### Plan de Contingencia
|
|
263
|
-
- Mantener SDK Expo actual como fallback
|
|
264
|
-
- Implementación gradual por módulos
|
|
265
|
-
- Rollback strategy para cada fase
|
|
266
|
-
|
|
267
|
-
## Métricas de Éxito
|
|
268
|
-
|
|
269
|
-
### Técnicas
|
|
270
|
-
- [ ] 90%+ de cobertura de tests
|
|
271
|
-
- [ ] Bundle size < 50KB para web
|
|
272
|
-
- [ ] Performance similar o mejor que SDK actual
|
|
273
|
-
- [ ] Zero breaking changes en migración
|
|
274
|
-
|
|
275
|
-
### Negocio
|
|
276
|
-
- [ ] Reducción 50% tiempo de desarrollo de nuevas features
|
|
277
|
-
- [ ] Adopción 80%+ en proyectos existentes
|
|
278
|
-
- [ ] Feedback positivo de desarrolladores
|
|
279
|
-
- [ ] Mantenimiento más eficiente
|
|
280
|
-
|
|
281
|
-
## Timeline Estimado
|
|
282
|
-
|
|
283
|
-
| Fase | Duración | Entregables |
|
|
284
|
-
|------|----------|-------------|
|
|
285
|
-
| 1 | 2 semanas | Core setup, monorepo |
|
|
286
|
-
| 2 | 2 semanas | Expo adapters |
|
|
287
|
-
| 3 | 2 semanas | Web adapters |
|
|
288
|
-
| 4 | 2 semanas | Analytics & monetization |
|
|
289
|
-
| 5 | 2 semanas | Testing & docs |
|
|
290
|
-
| 6 | 2 semanas | Migration & release |
|
|
291
|
-
|
|
292
|
-
**Total: 12 semanas (3 meses)**
|
|
293
|
-
|
|
294
|
-
## Recursos Necesarios
|
|
295
|
-
|
|
296
|
-
### Equipo
|
|
297
|
-
- 1 Senior Developer (lead)
|
|
298
|
-
- 1 Frontend Developer (web specialist)
|
|
299
|
-
- 1 Mobile Developer (Expo specialist)
|
|
300
|
-
- 1 QA Engineer (testing)
|
|
301
|
-
|
|
302
|
-
### Herramientas
|
|
303
|
-
- Lerna/Nx para monorepo
|
|
304
|
-
- TypeScript
|
|
305
|
-
- Jest para testing
|
|
306
|
-
- GitHub Actions para CI/CD
|
|
307
|
-
- Storybook para component documentation
|
|
308
|
-
|
|
309
|
-
## Próximos Pasos Inmediatos
|
|
310
|
-
|
|
311
|
-
1. **Validar el plan** con stakeholders
|
|
312
|
-
2. **Configurar el monorepo** básico
|
|
313
|
-
3. **Crear POC** con un módulo simple (Utils)
|
|
314
|
-
4. **Definir APIs finales** para adaptadores
|
|
315
|
-
5. **Comenzar migración** del core
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
*Este plan está sujeto a revisiones basadas en feedback y descubrimientos durante la implementación.*
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Modal } from 'react-native';
|
|
3
|
-
import { AdaptyOnboardingView } from 'react-native-adapty/dist/ui';
|
|
4
|
-
import { View } from "react-native";
|
|
5
|
-
import Adapty from '../libraries/Adapty';
|
|
6
|
-
import * as config from '../../config';
|
|
7
|
-
|
|
8
|
-
class AdaptyOnboarding extends React.Component {
|
|
9
|
-
constructor(props) {
|
|
10
|
-
super(props);
|
|
11
|
-
this.state = {
|
|
12
|
-
onboarding: null,
|
|
13
|
-
isLoading: true,
|
|
14
|
-
};
|
|
15
|
-
this.remoteConfig = null;
|
|
16
|
-
this.currentStep = 0;
|
|
17
|
-
this.totalSteps = 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
componentDidMount() {
|
|
21
|
-
const { placementID, lang } = this.props;
|
|
22
|
-
this.loadOnboarding(placementID, lang);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async componentDidUpdate(prevProps) {
|
|
26
|
-
if (this.props.visible && !prevProps.visible) {
|
|
27
|
-
const { placementID, lang } = this.props;
|
|
28
|
-
await this.loadOnboarding(placementID, lang);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
loadOnboarding = async (placementID, lang) => {
|
|
33
|
-
try {
|
|
34
|
-
this.setState({ isLoading: true });
|
|
35
|
-
const onboarding = await Adapty.getOnboardingForPlacement(placementID, lang);
|
|
36
|
-
|
|
37
|
-
if (onboarding) {
|
|
38
|
-
this.remoteConfig = onboarding.remoteConfig || {};
|
|
39
|
-
|
|
40
|
-
if (this.props.onRemoteConfigLoaded && this.remoteConfig) {
|
|
41
|
-
config.DEBUG_MODE && console.log('Remote config loaded:', this.remoteConfig);
|
|
42
|
-
this.props.onRemoteConfigLoaded(this.remoteConfig);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this.setState({ onboarding, isLoading: false });
|
|
46
|
-
} else {
|
|
47
|
-
console.log('Onboarding not found for placement:', placementID, 'and language:', lang);
|
|
48
|
-
this.setState({ isLoading: false });
|
|
49
|
-
if (this.props.onError) {
|
|
50
|
-
this.props.onError(new Error('Onboarding not found'), this.remoteConfig);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.log('Error loading onboarding:', error);
|
|
55
|
-
this.setState({ isLoading: false });
|
|
56
|
-
if (this.props.onError) {
|
|
57
|
-
this.props.onError(error, this.remoteConfig);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// --------------------------------------- Event Handlers ---------------------------------------
|
|
63
|
-
handleCustom = (actionId, meta) => {
|
|
64
|
-
if (this.props.onCustom) {
|
|
65
|
-
this.props.onCustom(actionId, meta, this.remoteConfig);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
handleClose = (actionId, meta) => {
|
|
70
|
-
const isLastStep = this.totalSteps > 0 && this.currentStep === this.totalSteps - 1;
|
|
71
|
-
|
|
72
|
-
config.DEBUG_MODE && console.log('Onboarding close attempt AdaptyOnboarding:', {actionId, meta, currentStep: this.currentStep, totalSteps: this.totalSteps, isLastStep });
|
|
73
|
-
|
|
74
|
-
if (this.props.onClose) {
|
|
75
|
-
this.props.onClose(actionId, meta, this.remoteConfig);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.currentStep = 0;
|
|
79
|
-
this.totalSteps = 0;
|
|
80
|
-
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
handleStateUpdated = (action, meta) => {
|
|
85
|
-
if (meta && typeof meta === 'object') {
|
|
86
|
-
this.currentStep = meta.screen_index ?? this.currentStep;
|
|
87
|
-
this.totalSteps = meta.total_screens ?? this.totalSteps;
|
|
88
|
-
|
|
89
|
-
config.DEBUG_MODE && console.log('State updated AdaptyOnboarding:', {action, meta, currentStep: this.currentStep, totalSteps: this.totalSteps});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (this.props.onStateUpdated) {
|
|
93
|
-
this.props.onStateUpdated(action, meta, this.remoteConfig);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
handleError = (error) => {
|
|
98
|
-
console.log('Onboarding error:', error);
|
|
99
|
-
|
|
100
|
-
if (this.props.onError) {
|
|
101
|
-
this.props.onError(error, this.remoteConfig);
|
|
102
|
-
}
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
handleAnalytics = (event, data) => {
|
|
107
|
-
config.DEBUG_MODE && console.log('Onboarding analytics AdaptyOnboarding:', event, data);
|
|
108
|
-
|
|
109
|
-
if (this.props.onAnalytics) {
|
|
110
|
-
this.props.onAnalytics(event, data, this.remoteConfig);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
handlePaywall = (actionId, meta) => {
|
|
115
|
-
config.DEBUG_MODE && console.log('Onboarding paywall AdaptyOnboarding:', actionId, meta);
|
|
116
|
-
|
|
117
|
-
if (this.props.onPaywall) {
|
|
118
|
-
this.props.onPaywall(this.remoteConfig);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
handleFinishedLoading = (meta) => {
|
|
123
|
-
config.DEBUG_MODE && console.log('Onboarding finished loading AdaptyOnboarding:', meta);
|
|
124
|
-
|
|
125
|
-
if (this.props.onFinishedLoading) {
|
|
126
|
-
this.props.onFinishedLoading(meta, this.remoteConfig);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// --------------------------------------- Event Handlers ---------------------------------------
|
|
130
|
-
|
|
131
|
-
render() {
|
|
132
|
-
const { visible } = this.props;
|
|
133
|
-
const { onboarding, isLoading } = this.state;
|
|
134
|
-
|
|
135
|
-
if (!visible) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const content = (
|
|
140
|
-
<View style={styles.container}>
|
|
141
|
-
{!isLoading && onboarding ? (
|
|
142
|
-
<AdaptyOnboardingView
|
|
143
|
-
onboarding={onboarding}
|
|
144
|
-
style={styles.onboardingView}
|
|
145
|
-
eventHandlers={{
|
|
146
|
-
onAnalytics: this.handleAnalytics,
|
|
147
|
-
onClose: this.handleClose,
|
|
148
|
-
onCustom: this.handleCustom,
|
|
149
|
-
onPaywall: this.handlePaywall,
|
|
150
|
-
onStateUpdated: this.handleStateUpdated,
|
|
151
|
-
onFinishedLoading: this.handleFinishedLoading,
|
|
152
|
-
onError: this.handleError,
|
|
153
|
-
}}
|
|
154
|
-
/>
|
|
155
|
-
) : null}
|
|
156
|
-
</View>
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
<Modal
|
|
161
|
-
visible={visible}
|
|
162
|
-
animationType="slide"
|
|
163
|
-
presentationStyle="fullScreen"
|
|
164
|
-
onRequestClose={() => {
|
|
165
|
-
config.DEBUG_MODE && console.log('Modal onRequestClose prevented');
|
|
166
|
-
return false;
|
|
167
|
-
}}
|
|
168
|
-
>
|
|
169
|
-
{content}
|
|
170
|
-
</Modal>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const styles = {
|
|
176
|
-
container: {
|
|
177
|
-
backgroundColor: '#000',
|
|
178
|
-
width: '100%',
|
|
179
|
-
height: '100%',
|
|
180
|
-
},
|
|
181
|
-
onboardingView: {
|
|
182
|
-
flex: 1,
|
|
183
|
-
},
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export default AdaptyOnboarding;
|