@upeex/ads-sdk 1.1.23 → 1.1.25

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/README.md CHANGED
@@ -1,97 +1,16 @@
1
1
  # SDK-apps-React-native
2
- SDK oficial da Upeex para exibir anúncios em aplicativos React Native e Expo.
2
+ SDK para aplicaçõe React Native rodarem anúncios:
3
3
 
4
- ## Instalação
4
+ # Comando para inciar
5
+ npm i @upeex/ads-sdk
5
6
 
6
- Instale o pacote e suas dependências via npm:
7
+ ### Como importar
7
8
 
8
- ```bash
9
- npm install @upeex/ads-sdk
10
- ```
11
-
12
- ## Como Usar
13
-
14
- ### Importação
15
-
16
- ```javascript
9
+ ```js
17
10
  import AdBanner from '@upeex/ads-sdk';
18
- ```
19
-
20
- ### 1. Banner Simples (Padrão)
21
-
22
- Ideal para exibir em listas ou rodapés. O anúncio carrega no tamanho especificado ou padrão.
23
-
24
- ```javascript
25
- <AdBanner
26
- client="SEU_ID_CLIENTE"
27
- slot="SEU_ID_SLOT"
28
- />
29
- ```
30
-
31
- ### 2. Anúncio Popup (Modal)
32
-
33
- Exibe um anúncio em tela cheia (modal) sobrepondo o conteúdo.
34
- **Comportamento:**
35
- - Abre automaticamente se houver anúncio disponível.
36
- - **Bloqueio de 5 segundos:** O usuário deve aguardar 5 segundos antes de fechar.
37
- - **Spinner:** Um indicador de carregamento aparece durante o bloqueio.
38
- - **Sem Auto-Refresh:** O popup carrega apenas uma vez.
39
- - **Visual:** Ocupa toda a tela (transparente na StatusBar) e a imagem é redimensionada para 90% da largura.
40
-
41
- #### ⚠️ Onde Colocar o Popup (Importante!)
42
-
43
- Para garantir que o popup abra corretamente e não sobreponha outros Modals de forma errada, **coloque o componente no final do seu container principal**, logo antes de fechar a view da tela.
44
-
45
- **Exemplo Correto:**
46
-
47
- ```javascript
48
- return (
49
- <View style={{ flex: 1 }}> {/* Seu Container Principal (View, SafeAreaView, etc) */}
50
- <Header />
51
- <Conteudo />
52
-
53
- {/* Outros Modals do seu App */}
54
- <Modal visible={isLoading}>...</Modal>
55
-
56
- {/* ✅ COLOCAR AQUI, NO FINAL: */}
57
- <AdBanner
58
- client="SEU_ID_CLIENTE"
59
- slot="SEU_ID_SLOT"
60
- typeads="popup"
61
- />
62
- </View>
63
- );
64
- ```
65
-
66
- ```
67
-
68
- **Evite colocar dentro de:**
69
- - Componentes que são desmontados condicionalmente (`{show && <AdBanner />}`).
70
- - Headers ou Footers com `position: absolute` ou `zIndex` restritivo.
71
-
72
- #### ⚠️ Conflito com Outros Modals (Loading, Alertas)
73
-
74
- O React Native pode ter problemas ao tentar exibir **dois Modals ao mesmo tempo** (ex: um Loading Spinner e o Anúncio Popup).
75
- - Se o seu app abre um Modal de "Carregando..." ao entrar na tela, aguarde ele fechar antes de renderizar o anúncio, ou use o anúncio como o próprio loading inicial.
76
- - O SDK possui `zIndex` alto (9999), mas o sistema operacional (especialmente Android) pode priorizar o último Modal aberto na árvore de componentes.
77
-
78
-
79
- ### Props Disponíveis
80
-
81
- | Prop | Tipo | Descrição |
82
- |Data | --- | --- |
83
- | `client` | `string` | **Obrigatório**. ID do cliente fornecido pela Upeex. |
84
- | `slot` | `string` | **Obrigatório**. ID do slot (espaço) do anúncio. |
85
- | `typeads` | `'banner' \| 'popup'` | Define o formato. Padrão é `'banner'`. |
86
- | `theme` | `'light' \| 'dark'` | Define o tema (cores de fundo). Padrão `'light'`. |
87
- | `baseUrl` | `string` | URL base da API (opcional). |
88
- | `debug` | `boolean` | Ativa logs no console (avisa se o componente foi desmontado erradamente). |
89
-
90
- ## Solução de Problemas
91
11
 
92
- - **Erro "Cannot find module 'react-native'"**: Certifique-se de que `react` e `react-native` estão instalados no seu projeto.
93
- - **Popup não abre**:
94
- 1. Verifique se `typeads="popup"`.
95
- 2. Ative `debug={true}` e olhe o console. Se aparecer "Nenhum anúncio disponível", é normal.
96
- 3. Se aparecer o aviso "O componente foi desmontado...", mova o `<AdBanner />` para a raiz da tela.
97
- - **Sobreposição**: O popup possui `zIndex: 9999` e `elevation: 11`. Se ainda estiver atrás de algum elemento, verifique se o pai não tem um `zIndex` maior.
12
+ // Carregar anúncio
13
+ <AdBanner
14
+ client="SEU_CLIENTE"
15
+ slot="SEU_SLOT"
16
+ /
@@ -8,6 +8,5 @@ type Props = {
8
8
  style?: object;
9
9
  debug?: boolean;
10
10
  };
11
- export default function AdBanner({ client, slot, baseUrl, theme, typeAds, typeads, // Receive alias
12
- style, debug, }: Props): any;
11
+ export default function AdBanner({ client, slot, baseUrl, theme, typeAds, typeads, style, debug, }: Props): import("react/jsx-runtime").JSX.Element;
13
12
  export {};
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import React, { useState, useEffect } from 'react';
3
- import { Platform, Dimensions, StyleSheet, View, Text, Modal, TouchableWithoutFeedback, TouchableOpacity, ActivityIndicator, Image, Linking } from 'react-native';
2
+ import { useState, useRef, useEffect } from 'react';
3
+ import { Platform, Dimensions, Animated, StyleSheet, Modal, View, ActivityIndicator, TouchableOpacity, Text, Image, Linking } from 'react-native';
4
4
 
5
5
  async function getSignals() {
6
6
  var _a, _b, _c, _d, _e, _f;
@@ -79,22 +79,41 @@ async function fetchAd({ baseUrl, client, slot, debug = false, typeAds, }) {
79
79
  return ad;
80
80
  }
81
81
 
82
- function AdBanner({ client, slot, baseUrl = 'https://app.upeex.com.br/ad', theme = 'light', typeAds, typeads, // Receive alias
83
- style = {}, debug = false, }) {
82
+ // Default fallback ads shown when server returns an error
83
+ const DEFAULT_POPUP_IMAGE = 'https://app.upeex.com.br/files/show/ads_square.jpeg';
84
+ const DEFAULT_BANNER_IMAGE = 'https://app.upeex.com.br/files/show/ads_horizontal.jpg';
85
+ const DEFAULT_CLICK_URL = 'https://upeex.com/';
86
+ const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
87
+ // Workaround for TS2604: JSX element type 'Animated.View' does not have any construct or call signatures.
88
+ const AnimatedView = Animated.View;
89
+ function AdBanner({ client, slot, baseUrl = 'https://app.upeex.com.br/ad', theme = 'light', typeAds, typeads, style = {}, debug = false, }) {
84
90
  var _a;
85
91
  const [ad, setAd] = useState(null);
86
92
  const [error, setError] = useState(null);
87
93
  const [modalVisible, setModalVisible] = useState(false);
94
+ const [canClose, setCanClose] = useState(false);
95
+ const progressAnim = useRef(new Animated.Value(0)).current;
88
96
  const log = (...args) => {
89
97
  if (debug)
90
98
  console.log('[UPEEX ADS]', ...args);
91
99
  };
92
100
  const effectiveTypeAds = (_a = (typeAds || typeads)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
93
101
  const isPopup = effectiveTypeAds === 'popup';
94
- // Track visibility with ref to avoid dependency loops in useEffect
95
- const isVisibleRef = React.useRef(false);
102
+ // Start 5-second progress bar when popup becomes visible
96
103
  useEffect(() => {
97
- isVisibleRef.current = modalVisible;
104
+ if (modalVisible) {
105
+ setCanClose(false);
106
+ progressAnim.setValue(0);
107
+ Animated.timing(progressAnim, {
108
+ toValue: 1,
109
+ duration: 5000,
110
+ useNativeDriver: false,
111
+ }).start(({ finished }) => {
112
+ if (finished) {
113
+ setCanClose(true);
114
+ }
115
+ });
116
+ }
98
117
  }, [modalVisible]);
99
118
  useEffect(() => {
100
119
  let refreshTimer;
@@ -119,8 +138,7 @@ style = {}, debug = false, }) {
119
138
  if (isPopup) {
120
139
  setModalVisible(true);
121
140
  }
122
- // Only setup refresh if NOT a popup
123
- if (!isPopup && data.refresh && data.refresh > 0) {
141
+ if (data.refresh && data.refresh > 0) {
124
142
  log(`Refresh ativo: ${data.refresh}s`);
125
143
  refreshTimer = setTimeout(() => {
126
144
  log('Refazendo request do anúncio');
@@ -140,27 +158,41 @@ style = {}, debug = false, }) {
140
158
  return () => {
141
159
  if (refreshTimer)
142
160
  clearTimeout(refreshTimer);
143
- if (isPopup && isVisibleRef.current && debug) {
144
- console.warn('[UPEEX ADS] Aviso: O componente AdBanner (Popup) foi desmontado enquanto estava visível. Verifique se ele não está dentro de uma View que foi ocultada ou removida da tela.');
161
+ };
162
+ }, [client, slot, baseUrl]);
163
+ // ─── FALLBACK: default ad on error ────────────────────────────────────────
164
+ if (error) {
165
+ const fallbackImage = isPopup ? DEFAULT_POPUP_IMAGE : DEFAULT_BANNER_IMAGE;
166
+ const handleFallbackPress = () => {
167
+ if (Platform.OS === 'web') {
168
+ window.open(DEFAULT_CLICK_URL, '_blank');
169
+ }
170
+ else {
171
+ Linking.openURL(DEFAULT_CLICK_URL);
145
172
  }
146
173
  };
147
- }, [client, slot, baseUrl, effectiveTypeAds]); // Removed modalVisible to prevent loops
148
- // Popup close delay logic
149
- const [canClose, setCanClose] = useState(false);
150
- useEffect(() => {
151
- if (isPopup && modalVisible) {
152
- setCanClose(false);
153
- const timer = setTimeout(() => {
154
- setCanClose(true);
155
- }, 5000);
156
- return () => clearTimeout(timer);
174
+ if (isPopup) {
175
+ return (jsx(Modal, { transparent: true, animationType: "fade", visible: true, onRequestClose: () => { }, children: jsxs(View, { style: styles.modalBackground, children: [jsx(View, { style: styles.progressBarContainer, children: jsx(AnimatedView, { style: [
176
+ styles.progressBar,
177
+ {
178
+ width: progressAnim.interpolate({
179
+ inputRange: [0, 1],
180
+ outputRange: ['0%', '100%'],
181
+ }),
182
+ backgroundColor: '#fff',
183
+ },
184
+ ] }) }), !canClose && (jsx(View, { style: styles.spinnerOverlay, children: jsx(ActivityIndicator, { size: "small", color: "#fff" }) })), canClose && (jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => { }, children: jsx(Text, { style: styles.closeButtonText, children: "X" }) })), jsx(TouchableOpacity, { style: styles.fullScreenTouch, activeOpacity: 1, onPress: handleFallbackPress, children: jsx(Image, { source: { uri: fallbackImage }, style: styles.fullScreenImage, resizeMode: "contain" }) })] }) }));
157
185
  }
158
- }, [isPopup, modalVisible]);
186
+ // Banner fallback
187
+ return (jsx(TouchableOpacity, { activeOpacity: 0.9, style: [styles.container, theme === 'dark' && styles.dark, style], onPress: handleFallbackPress, children: jsx(Image, { source: { uri: fallbackImage }, style: { width: 320, height: 50, resizeMode: 'contain' } }) }));
188
+ }
189
+ // ──────────────────────────────────────────────────────────────────────────
190
+ if (!ad) {
191
+ return debug ? (jsx(View, { style: [styles.container, styles.loading], children: jsx(Text, { style: styles.loadingText, children: "Carregando an\u00FAncio\u2026" }) })) : null;
192
+ }
159
193
  const handlePress = () => {
160
194
  log('Clique no anúncio', ad.clickUrl);
161
- if (isPopup) {
162
- // Allow close on click even if timer not finished?
163
- // Usually ads allow clicking through immediately.
195
+ if (isPopup && canClose) {
164
196
  setModalVisible(false);
165
197
  }
166
198
  if (!ad.clickUrl)
@@ -172,93 +204,102 @@ style = {}, debug = false, }) {
172
204
  Linking.openURL(ad.clickUrl);
173
205
  }
174
206
  };
175
- const handleClose = () => {
176
- if (canClose) {
177
- setModalVisible(false);
178
- }
179
- };
180
- if (error) {
181
- return debug ? (jsx(View, { style: [styles.upeexContainer, styles.upeexDebug], children: jsx(Text, { style: styles.upeexDebugText, children: error }) })) : null;
182
- }
183
- if (!ad) {
184
- return debug ? (jsx(View, { style: [styles.upeexContainer, styles.upeexLoading], children: jsx(Text, { style: styles.upeexLoadingText, children: "Carregando an\u00FAncio\u2026" }) })) : null;
185
- }
207
+ // ─── POPUP ─────────────────────────────────────────────────────────────────
186
208
  if (isPopup) {
187
- return (jsx(Modal, { transparent: true, visible: modalVisible, onRequestClose: handleClose, animationType: "fade", statusBarTranslucent: true, hardwareAccelerated: true, children: jsx(TouchableWithoutFeedback, { onPress: handleClose, children: jsx(View, { style: styles.upeexOverlay, children: jsx(TouchableWithoutFeedback, { onPress: () => { }, children: jsxs(View, { style: [
188
- styles.upeexPopupContainer,
189
- theme === 'dark' && styles.upeexDark,
190
- ], children: [canClose ? (jsx(TouchableOpacity, { onPress: handleClose, style: styles.upeexCloseButton, children: jsx(Text, { style: styles.upeexCloseText, children: "\u2715" }) })) : (jsx(View, { style: styles.upeexCloseButton, children: jsx(ActivityIndicator, { size: "small", color: "#999" }) })), jsx(TouchableOpacity, { activeOpacity: 0.9, onPress: handlePress, children: jsx(Image, { source: { uri: ad.image }, style: {
191
- width: Dimensions.get('window').width * 0.9,
192
- height: Dimensions.get('window').height * 0.8,
193
- resizeMode: 'contain',
194
- } }) })] }) }) }) }) }));
209
+ return (jsx(Modal, { transparent: true, animationType: "fade", visible: modalVisible, onRequestClose: () => canClose && setModalVisible(false), children: jsxs(View, { style: styles.modalBackground, children: [jsx(View, { style: styles.progressBarContainer, children: jsx(AnimatedView, { style: [
210
+ styles.progressBar,
211
+ {
212
+ width: progressAnim.interpolate({
213
+ inputRange: [0, 1],
214
+ outputRange: ['0%', '100%'],
215
+ }),
216
+ backgroundColor: '#fff',
217
+ },
218
+ ] }) }), !canClose && (jsx(View, { style: styles.spinnerOverlay, children: jsx(ActivityIndicator, { size: "small", color: "#fff" }) })), canClose && (jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setModalVisible(false), children: jsx(Text, { style: styles.closeButtonText, children: "X" }) })), jsx(TouchableOpacity, { style: styles.fullScreenTouch, activeOpacity: 1, onPress: handlePress, children: jsx(Image, { source: { uri: ad.image }, style: styles.fullScreenImage, resizeMode: "contain" }) })] }) }));
195
219
  }
196
- return (jsx(TouchableOpacity, { activeOpacity: 0.9, style: [
197
- styles.upeexContainer,
198
- theme === 'dark' && styles.upeexDark,
199
- style,
200
- ], onPress: handlePress, children: jsx(Image, { source: { uri: ad.image }, style: {
220
+ // ─── BANNER ────────────────────────────────────────────────────────────────
221
+ return (jsx(TouchableOpacity, { activeOpacity: 0.9, style: [styles.container, theme === 'dark' && styles.dark, style], onPress: handlePress, children: jsx(Image, { source: { uri: ad.image }, style: {
201
222
  width: ad.width || 320,
202
223
  height: ad.height || 50,
203
224
  resizeMode: 'contain',
204
225
  } }) }));
205
226
  }
206
227
  const styles = StyleSheet.create({
207
- upeexContainer: {
228
+ container: {
208
229
  backgroundColor: '#f2f2f2',
209
230
  padding: 8,
210
231
  alignItems: 'center',
211
232
  justifyContent: 'center',
212
233
  },
213
- upeexDark: {
234
+ dark: {
214
235
  backgroundColor: '#121212',
215
236
  },
216
- upeexLoading: {
237
+ loading: {
217
238
  backgroundColor: '#fafafa',
218
239
  },
219
- upeexLoadingText: {
240
+ loadingText: {
220
241
  fontSize: 11,
221
242
  color: '#999',
222
243
  },
223
- upeexDebug: {
244
+ debug: {
224
245
  backgroundColor: '#ffecec',
225
246
  },
226
- upeexDebugText: {
247
+ debugText: {
227
248
  fontSize: 11,
228
249
  color: '#c00',
229
250
  },
230
- upeexOverlay: {
251
+ // Modal / Popup
252
+ modalBackground: {
231
253
  flex: 1,
232
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
254
+ backgroundColor: 'rgba(0,0,0,0.9)',
233
255
  justifyContent: 'center',
234
256
  alignItems: 'center',
235
- zIndex: 9999,
236
- elevation: 10,
237
257
  },
238
- upeexPopupContainer: {
239
- backgroundColor: 'transparent',
240
- padding: 0,
258
+ fullScreenTouch: {
259
+ flex: 1,
260
+ width: '100%',
261
+ height: '100%',
262
+ justifyContent: 'center',
241
263
  alignItems: 'center',
242
- position: 'relative',
243
- zIndex: 10000,
244
264
  },
245
- upeexCloseButton: {
265
+ fullScreenImage: {
266
+ width: '90%',
267
+ height: '90%',
268
+ },
269
+ closeButton: {
246
270
  position: 'absolute',
247
- top: -10,
248
- right: -10,
249
- zIndex: 1,
250
- backgroundColor: '#fff',
251
- borderRadius: 15,
252
- width: 30,
253
- height: 30,
271
+ top: 50,
272
+ right: 20,
273
+ backgroundColor: 'rgba(0,0,0,0.6)',
274
+ width: 40,
275
+ height: 40,
276
+ borderRadius: 20,
254
277
  justifyContent: 'center',
255
278
  alignItems: 'center',
256
- elevation: 5,
279
+ zIndex: 999,
257
280
  },
258
- upeexCloseText: {
281
+ closeButtonText: {
282
+ color: '#fff',
259
283
  fontSize: 18,
260
284
  fontWeight: 'bold',
261
- color: '#333',
285
+ },
286
+ progressBarContainer: {
287
+ position: 'absolute',
288
+ top: 0,
289
+ left: 0,
290
+ width: '100%',
291
+ height: 5,
292
+ backgroundColor: 'rgba(255,255,255,0.3)',
293
+ zIndex: 1000,
294
+ },
295
+ progressBar: {
296
+ height: '100%',
297
+ },
298
+ spinnerOverlay: {
299
+ position: 'absolute',
300
+ top: 12,
301
+ right: 20,
302
+ zIndex: 1000,
262
303
  },
263
304
  });
264
305
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upeex/ads-sdk",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "description": "Upeex Ads SDK for React Native and universal apps",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,10 +27,11 @@
27
27
  "author": "Upeex",
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "@upeex/ads-sdk": "^1.1.10",
31
30
  "expo-localization": "^17.0.8"
32
31
  },
33
32
  "devDependencies": {
33
+ "@types/react": "^19.2.13",
34
+ "@types/react-native": "^0.73.0",
34
35
  "rollup": "^4.57.0",
35
36
  "rollup-plugin-typescript2": "^0.36.0",
36
37
  "tslib": "^2.8.1",