apps-sdk 1.1.30 → 1.1.32
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/EJEMPLO_USO_SDK.md +281 -0
- package/PLAN_SDK_UNIFICADO.md +319 -0
- package/package.json +1 -2
- package/src/libraries/Adapty.js +18 -0
- package/src/libraries/Notifications.js +1 -1
- package/types/index.d.ts +4 -2
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Ejemplo de Uso del SDK Unificado
|
|
2
|
+
|
|
3
|
+
## Función Storage - Mismo Código, Diferentes Plataformas
|
|
4
|
+
|
|
5
|
+
### 📱 Uso desde App Expo/React Native
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import SDK from '@apps-sdk/expo';
|
|
9
|
+
|
|
10
|
+
// Inicializar SDK
|
|
11
|
+
await SDK.init();
|
|
12
|
+
|
|
13
|
+
// Guardar datos
|
|
14
|
+
await SDK.storage.storeData('user_preferences', {
|
|
15
|
+
theme: 'dark',
|
|
16
|
+
language: 'es',
|
|
17
|
+
notifications: true
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Obtener datos
|
|
21
|
+
const preferences = await SDK.storage.getData('user_preferences');
|
|
22
|
+
console.log(preferences); // { theme: 'dark', language: 'es', notifications: true }
|
|
23
|
+
|
|
24
|
+
// Guardar imagen en galería (funcionalidad móvil)
|
|
25
|
+
await SDK.storage.handleDownloadImageToGallery('file://path/to/image.jpg');
|
|
26
|
+
|
|
27
|
+
// Comprimir imagen
|
|
28
|
+
const compressedUri = await SDK.storage.compressImage('file://image.jpg', 2); // 2MB max
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 🌐 Uso desde App Web/Next.js
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import SDK from '@apps-sdk/web';
|
|
35
|
+
|
|
36
|
+
// Inicializar SDK
|
|
37
|
+
await SDK.init();
|
|
38
|
+
|
|
39
|
+
// Guardar datos (MISMA API)
|
|
40
|
+
await SDK.storage.storeData('user_preferences', {
|
|
41
|
+
theme: 'dark',
|
|
42
|
+
language: 'es',
|
|
43
|
+
notifications: true
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Obtener datos (MISMA API)
|
|
47
|
+
const preferences = await SDK.storage.getData('user_preferences');
|
|
48
|
+
console.log(preferences); // { theme: 'dark', language: 'es', notifications: true }
|
|
49
|
+
|
|
50
|
+
// Descargar imagen (adaptado para web)
|
|
51
|
+
await SDK.storage.handleDownloadImage('https://example.com/image.jpg', 'my-image');
|
|
52
|
+
|
|
53
|
+
// Comprimir imagen (usando Canvas API)
|
|
54
|
+
const compressedBlob = await SDK.storage.compressImage(file, 2); // 2MB max
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 🔧 Implementación Interna (Cómo Funciona)
|
|
58
|
+
|
|
59
|
+
### Core Compartido (`@apps-sdk/core`)
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
// src/storage/StorageCore.js
|
|
63
|
+
export class StorageCore {
|
|
64
|
+
constructor(adapter) {
|
|
65
|
+
this.adapter = adapter; // Inyección de dependencia
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async storeData(key, value) {
|
|
69
|
+
const serialized = JSON.stringify(value);
|
|
70
|
+
return await this.adapter.setItem(key, serialized);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async getData(key) {
|
|
74
|
+
const serialized = await this.adapter.getItem(key);
|
|
75
|
+
return serialized ? JSON.parse(serialized) : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async removeData(key) {
|
|
79
|
+
return await this.adapter.removeItem(key);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Funciones específicas que delegan al adaptador
|
|
83
|
+
async compressImage(source, maxSizeMB) {
|
|
84
|
+
return await this.adapter.compressImage(source, maxSizeMB);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async handleDownloadImage(source, filename) {
|
|
88
|
+
return await this.adapter.downloadImage(source, filename);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Adaptador Expo (`@apps-sdk/expo`)
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// src/adapters/StorageAdapter.js
|
|
97
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
98
|
+
import * as MediaLibrary from 'expo-media-library';
|
|
99
|
+
import { ImageManipulator } from 'expo-image-manipulator';
|
|
100
|
+
|
|
101
|
+
export class ExpoStorageAdapter {
|
|
102
|
+
// Implementación básica
|
|
103
|
+
async setItem(key, value) {
|
|
104
|
+
return await AsyncStorage.setItem(key, value);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getItem(key) {
|
|
108
|
+
return await AsyncStorage.getItem(key);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async removeItem(key) {
|
|
112
|
+
return await AsyncStorage.removeItem(key);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Funcionalidades específicas de móvil
|
|
116
|
+
async compressImage(imageUri, maxSizeMB) {
|
|
117
|
+
const qualities = [0.8, 0.6, 0.4, 0.2];
|
|
118
|
+
for (const quality of qualities) {
|
|
119
|
+
const compressed = await ImageManipulator.manipulateAsync(
|
|
120
|
+
imageUri,
|
|
121
|
+
[],
|
|
122
|
+
{ compress: quality, format: 'jpeg' }
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const fileSize = await this.getFileSize(compressed.uri);
|
|
126
|
+
if (fileSize < maxSizeMB) {
|
|
127
|
+
return compressed.uri;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async downloadImage(source, filename) {
|
|
134
|
+
// Lógica específica para guardar en galería móvil
|
|
135
|
+
const { status } = await MediaLibrary.requestPermissionsAsync();
|
|
136
|
+
if (status === 'granted') {
|
|
137
|
+
return await MediaLibrary.createAssetAsync(source);
|
|
138
|
+
}
|
|
139
|
+
throw new Error('Gallery permission denied');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Adaptador Web (`@apps-sdk/web`)
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// src/adapters/StorageAdapter.js
|
|
148
|
+
export class WebStorageAdapter {
|
|
149
|
+
// Implementación básica
|
|
150
|
+
async setItem(key, value) {
|
|
151
|
+
localStorage.setItem(key, value);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getItem(key) {
|
|
155
|
+
return localStorage.getItem(key);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async removeItem(key) {
|
|
159
|
+
localStorage.removeItem(key);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Funcionalidades adaptadas para web
|
|
163
|
+
async compressImage(file, maxSizeMB) {
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
const canvas = document.createElement('canvas');
|
|
166
|
+
const ctx = canvas.getContext('2d');
|
|
167
|
+
const img = new Image();
|
|
168
|
+
|
|
169
|
+
img.onload = () => {
|
|
170
|
+
// Lógica de compresión usando Canvas API
|
|
171
|
+
canvas.width = img.width;
|
|
172
|
+
canvas.height = img.height;
|
|
173
|
+
ctx.drawImage(img, 0, 0);
|
|
174
|
+
|
|
175
|
+
const qualities = [0.8, 0.6, 0.4, 0.2];
|
|
176
|
+
for (const quality of qualities) {
|
|
177
|
+
canvas.toBlob((blob) => {
|
|
178
|
+
if (blob.size / 1024 / 1024 < maxSizeMB) {
|
|
179
|
+
resolve(blob);
|
|
180
|
+
}
|
|
181
|
+
}, 'image/jpeg', quality);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
img.src = URL.createObjectURL(file);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async downloadImage(source, filename) {
|
|
190
|
+
// Lógica específica para descargar en navegador
|
|
191
|
+
const response = await fetch(source);
|
|
192
|
+
const blob = await response.blob();
|
|
193
|
+
|
|
194
|
+
const url = window.URL.createObjectURL(blob);
|
|
195
|
+
const a = document.createElement('a');
|
|
196
|
+
a.href = url;
|
|
197
|
+
a.download = filename;
|
|
198
|
+
document.body.appendChild(a);
|
|
199
|
+
a.click();
|
|
200
|
+
document.body.removeChild(a);
|
|
201
|
+
window.URL.revokeObjectURL(url);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Inicialización por Plataforma
|
|
207
|
+
|
|
208
|
+
#### Expo Package (`@apps-sdk/expo/index.js`)
|
|
209
|
+
```javascript
|
|
210
|
+
import { StorageCore } from '@apps-sdk/core';
|
|
211
|
+
import { ExpoStorageAdapter } from './adapters/StorageAdapter';
|
|
212
|
+
|
|
213
|
+
const storageAdapter = new ExpoStorageAdapter();
|
|
214
|
+
const storage = new StorageCore(storageAdapter);
|
|
215
|
+
|
|
216
|
+
export default {
|
|
217
|
+
storage,
|
|
218
|
+
// ... otros módulos
|
|
219
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Web Package (`@apps-sdk/web/index.js`)
|
|
223
|
+
```javascript
|
|
224
|
+
import { StorageCore } from '@apps-sdk/core';
|
|
225
|
+
import { WebStorageAdapter } from './adapters/StorageAdapter';
|
|
226
|
+
|
|
227
|
+
const storageAdapter = new WebStorageAdapter();
|
|
228
|
+
const storage = new StorageCore(storageAdapter);
|
|
229
|
+
|
|
230
|
+
export default {
|
|
231
|
+
storage,
|
|
232
|
+
// ... otros módulos
|
|
233
|
+
};
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## 🎯 Ventajas de Esta Arquitectura
|
|
237
|
+
|
|
238
|
+
### Para el Desarrollador
|
|
239
|
+
- **API Idéntica** - Mismo código en ambas plataformas
|
|
240
|
+
- **IntelliSense** - Mismos tipos TypeScript
|
|
241
|
+
- **Documentación única** - Una sola referencia
|
|
242
|
+
|
|
243
|
+
### Para el Mantenimiento
|
|
244
|
+
- **Lógica centralizada** - Bugs se arreglan una vez
|
|
245
|
+
- **Testing unificado** - Tests del core sirven para ambas plataformas
|
|
246
|
+
- **Features sincronizadas** - Nuevas funcionalidades llegan a ambas plataformas
|
|
247
|
+
|
|
248
|
+
### Ejemplo de Migración
|
|
249
|
+
|
|
250
|
+
#### Antes (SDK actual)
|
|
251
|
+
```javascript
|
|
252
|
+
// Solo funciona en Expo
|
|
253
|
+
import SDK from 'apps-sdk';
|
|
254
|
+
await SDK.storage.storeData('key', 'value');
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Después (SDK unificado)
|
|
258
|
+
```javascript
|
|
259
|
+
// Funciona en Expo
|
|
260
|
+
import SDK from '@apps-sdk/expo';
|
|
261
|
+
await SDK.storage.storeData('key', 'value');
|
|
262
|
+
|
|
263
|
+
// Funciona en Web (MISMA API)
|
|
264
|
+
import SDK from '@apps-sdk/web';
|
|
265
|
+
await SDK.storage.storeData('key', 'value');
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## 📦 Instalación
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Para proyectos Expo
|
|
272
|
+
npm install @apps-sdk/expo
|
|
273
|
+
|
|
274
|
+
# Para proyectos Web/Next.js
|
|
275
|
+
npm install @apps-sdk/web
|
|
276
|
+
|
|
277
|
+
# Para proyectos que necesiten ambos (monorepo)
|
|
278
|
+
npm install @apps-sdk/core @apps-sdk/expo @apps-sdk/web
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Esta arquitectura permite que el 90% del código de tu aplicación sea idéntico entre plataformas, mientras que las diferencias específicas se manejan transparentemente en los adaptadores.
|
|
@@ -0,0 +1,319 @@
|
|
|
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.*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apps-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.32",
|
|
4
4
|
"description": "Apps SDK",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
"@react-native-community/netinfo": "11.4.1",
|
|
15
15
|
"@react-native-voice/voice": "^3.2.4",
|
|
16
16
|
"apps-sdk": "^1.0.120",
|
|
17
|
-
"crypto-es": "^2.1.0",
|
|
18
17
|
"expo": "~52.0.38",
|
|
19
18
|
"expo-constants": "~17.0.5",
|
|
20
19
|
"expo-device": "~7.0.2",
|
package/src/libraries/Adapty.js
CHANGED
|
@@ -82,6 +82,11 @@ class Adapty {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
async checkPlacementExists(placementID, lang) {
|
|
86
|
+
const paywall = await this.getPaywallForPlacement(placementID, lang);
|
|
87
|
+
return paywall !== null;
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
async preloadPaywall(placementID, lang) {
|
|
86
91
|
try {
|
|
87
92
|
const paywall = await this.getPaywallForPlacement(placementID, lang);
|
|
@@ -101,6 +106,19 @@ class Adapty {
|
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
async closePaywall() {
|
|
110
|
+
const paywallView = await adapty.getCurrentPaywallView();
|
|
111
|
+
try {
|
|
112
|
+
if (paywallView) {
|
|
113
|
+
paywallView.dismiss();
|
|
114
|
+
} else {
|
|
115
|
+
console.warn('Error closing paywall: paywall view is null');
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error closing paywall:', error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
async showPaywallPreloaded(paywallView, eventHandlers = {}) {
|
|
105
123
|
try {
|
|
106
124
|
if (paywallView) {
|
|
@@ -25,9 +25,9 @@ class NotificationsPush {
|
|
|
25
25
|
|
|
26
26
|
Notifications.addNotificationResponseReceivedListener(response => {
|
|
27
27
|
console.log('addNotificationResponseReceivedListener',JSON.stringify(response));
|
|
28
|
+
Storage.storeData('PUSH_ACTION', response.notification.request.content.data);
|
|
28
29
|
Networking.sendEvent('action', 'click_notification', {"date_notification": response.notification.date, "content": response.notification.request.content});
|
|
29
30
|
MixPanel.trackEvent('Push Notification CTA' ,{ "action": response.notification.request.content.data.action || "", "placement": response.notification.request.content.data.placement || "" });
|
|
30
|
-
Storage.storeData('PUSH_ACTION', response.notification.request.content.data);
|
|
31
31
|
});
|
|
32
32
|
};
|
|
33
33
|
|
package/types/index.d.ts
CHANGED
|
@@ -215,12 +215,14 @@ declare module 'apps-sdk' {
|
|
|
215
215
|
getPaywallForPlacement(placementID: string, lang: string): Promise<any>;
|
|
216
216
|
isActivated(): Promise<boolean>;
|
|
217
217
|
createPaywallView(paywall: any): Promise<any>;
|
|
218
|
-
showPaywall(placementID: any, lang:
|
|
219
|
-
preloadPaywall(placementID: any, lang:
|
|
218
|
+
showPaywall(placementID: any, lang: string, eventHandlers?: PaywallEventHandlers): Promise<void>;
|
|
219
|
+
preloadPaywall(placementID: any, lang: string): Promise<any>
|
|
220
220
|
showPaywallPreloaded(paywallView: any, eventHandlers?: PaywallEventHandlers): Promise<void>;
|
|
221
|
+
checkPlacementExists(placementID: any, lang: string): Promise<boolean>;
|
|
221
222
|
getProfile(): Promise<any>;
|
|
222
223
|
updateAdjustAttributionData(attribution: any): Promise<void>;
|
|
223
224
|
setAdjustIntegrationIdentifier(adjustIntegrationIdentifier: string): Promise<void>;
|
|
225
|
+
closePaywall(): Promise<void>;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
export class TrackingTransparency {
|