apps-sdk 1.1.58 → 1.1.60

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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apps-sdk",
3
- "version": "1.1.58",
3
+ "version": "1.1.60",
4
4
  "description": "Apps SDK",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
 
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>;
@@ -294,7 +278,6 @@ declare module 'apps-sdk' {
294
278
  session : Session;
295
279
  utils : Utils;
296
280
  paywall : PayWall;
297
- adaptyOnboarding : typeof AdaptyOnboarding;
298
281
  rating : Rating;
299
282
  adjust : AdJust;
300
283
  mixpanel : MixPanel;
@@ -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,217 +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.termsAccepted = false;
17
- this.currentStep = 0;
18
- this.totalSteps = 0;
19
- }
20
-
21
- componentDidMount() {
22
- const { placementID, lang } = this.props;
23
- this.loadOnboarding(placementID, lang);
24
- }
25
-
26
- async componentDidUpdate(prevProps) {
27
- if (this.props.visible && !prevProps.visible) {
28
- const { placementID, lang } = this.props;
29
- await this.loadOnboarding(placementID, lang);
30
- }
31
- }
32
-
33
- loadOnboarding = async (placementID, lang) => {
34
- try {
35
- this.setState({ isLoading: true });
36
- const onboarding = await Adapty.getOnboardingForPlacement(placementID, lang);
37
-
38
- if (onboarding) {
39
- this.remoteConfig = onboarding.remoteConfig || {};
40
-
41
- if (this.props.onRemoteConfigLoaded && this.remoteConfig) {
42
- config.DEBUG_MODE && console.log('Remote config loaded:', this.remoteConfig);
43
- this.props.onRemoteConfigLoaded(this.remoteConfig);
44
- }
45
-
46
- this.setState({ onboarding, isLoading: false });
47
- } else {
48
- console.log('Onboarding not found for placement:', placementID, 'and language:', lang);
49
- this.setState({ isLoading: false });
50
- if (this.props.onError) {
51
- this.props.onError(new Error('Onboarding not found'), this.remoteConfig);
52
- }
53
- }
54
- } catch (error) {
55
- console.log('Error loading onboarding:', error);
56
- this.setState({ isLoading: false });
57
- if (this.props.onError) {
58
- this.props.onError(error, this.remoteConfig);
59
- }
60
- }
61
- }
62
-
63
- // --------------------------------------- Event Handlers ---------------------------------------
64
- handleCustom = (actionId, meta) => {
65
- config.DEBUG_MODE && console.log('Onboarding custom action:', actionId, meta);
66
-
67
- if (actionId === 'accept_terms') {
68
- this.termsAccepted = true;
69
- config.DEBUG_MODE && console.log('Terms accepted');
70
- }
71
-
72
- if (this.props.onCustom) {
73
- this.props.onCustom(actionId, meta, this.remoteConfig);
74
- }
75
- }
76
-
77
- handleClose = (actionId, meta) => {
78
- const isLastStep = this.totalSteps > 0 && this.currentStep === this.totalSteps - 1;
79
- const canClose = this.termsAccepted && isLastStep;
80
-
81
- config.DEBUG_MODE && console.log('Onboarding close attempt:', {
82
- actionId,
83
- meta,
84
- termsAccepted: this.termsAccepted,
85
- currentStep: this.currentStep,
86
- totalSteps: this.totalSteps,
87
- isLastStep,
88
- canClose
89
- });
90
-
91
- if (!canClose) {
92
- config.DEBUG_MODE && console.log('Close prevented');
93
- return false;
94
- }
95
-
96
- config.DEBUG_MODE && console.log('Close allowed');
97
-
98
- if (this.props.onClose) {
99
- this.props.onClose(actionId, meta, this.remoteConfig);
100
- }
101
-
102
- this.termsAccepted = false;
103
- this.currentStep = 0;
104
- this.totalSteps = 0;
105
-
106
- return true;
107
- }
108
-
109
- handleStateUpdated = (action, meta) => {
110
- if (meta && typeof meta === 'object') {
111
- this.currentStep = meta.screen_index ?? this.currentStep;
112
- this.totalSteps = meta.total_screens ?? this.totalSteps;
113
-
114
- config.DEBUG_MODE && console.log('State updated:', {
115
- action,
116
- meta,
117
- currentStep: this.currentStep,
118
- totalSteps: this.totalSteps,
119
- termsAccepted: this.termsAccepted
120
- });
121
- }
122
-
123
- if (this.props.onStateUpdated) {
124
- this.props.onStateUpdated(action, meta, this.remoteConfig);
125
- }
126
- }
127
-
128
- handleError = (error) => {
129
- console.log('Onboarding error:', error);
130
-
131
- if (this.props.onError) {
132
- this.props.onError(error, this.remoteConfig);
133
- }
134
- return true;
135
- }
136
-
137
- handleAnalytics = (event, data) => {
138
- config.DEBUG_MODE && console.log('Onboarding analytics:', event, data);
139
-
140
- if (this.props.onAnalytics) {
141
- this.props.onAnalytics(event, data, this.remoteConfig);
142
- }
143
- }
144
-
145
- handlePaywall = (actionId, meta) => {
146
- config.DEBUG_MODE && console.log('Onboarding paywall:', actionId, meta);
147
-
148
- if (this.props.onPaywall) {
149
- this.props.onPaywall(this.remoteConfig);
150
- }
151
- }
152
-
153
- handleFinishedLoading = (meta) => {
154
- config.DEBUG_MODE && console.log('Onboarding finished loading', meta);
155
-
156
- if (this.props.onFinishedLoading) {
157
- this.props.onFinishedLoading(meta, this.remoteConfig);
158
- }
159
- }
160
- // --------------------------------------- Event Handlers ---------------------------------------
161
-
162
- render() {
163
- const { visible } = this.props;
164
- const { onboarding, isLoading } = this.state;
165
-
166
- if (!visible) {
167
- return null;
168
- }
169
-
170
- const content = (
171
- <View style={styles.container}>
172
- {!isLoading && onboarding ? (
173
- <AdaptyOnboardingView
174
- onboarding={onboarding}
175
- style={styles.onboardingView}
176
- eventHandlers={{
177
- onAnalytics: this.handleAnalytics,
178
- onClose: this.handleClose,
179
- onCustom: this.handleCustom,
180
- onPaywall: this.handlePaywall,
181
- onStateUpdated: this.handleStateUpdated,
182
- onFinishedLoading: this.handleFinishedLoading,
183
- onError: this.handleError,
184
- }}
185
- />
186
- ) : null}
187
- </View>
188
- );
189
-
190
- return (
191
- <Modal
192
- visible={visible}
193
- animationType="slide"
194
- presentationStyle="fullScreen"
195
- onRequestClose={() => {
196
- config.DEBUG_MODE && console.log('Modal onRequestClose prevented');
197
- return false;
198
- }}
199
- >
200
- {content}
201
- </Modal>
202
- );
203
- }
204
- }
205
-
206
- const styles = {
207
- container: {
208
- backgroundColor: '#000',
209
- width: '100%',
210
- height: '100%',
211
- },
212
- onboardingView: {
213
- flex: 1,
214
- },
215
- }
216
-
217
- export default AdaptyOnboarding;