API del puente de JavaScript
Anuncios
Mostrar un anuncio intersticial o con recompensa en el momento que elijas, e indicarle al shell cuándo empieza y termina una navegación para que no interrumpa ninguna.
AppMintAds.appOpen ayudante#
window.AppMintAds.appOpen.setEnabled(on)
Ejemplo
Pausa o reanuda el anuncio de apertura de la app, el anuncio de pantalla completa que se muestra al iniciar y cuando el usuario vuelve de otra app.
Devuelve: nada. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio de apertura de la app.
Ponlo en pausa mientras el usuario sale de la app a propósito por un momento (una app de pagos, la cámara, una hoja para compartir), para que al volver no se muestre un anuncio:
var ads = window.AppMintAds;
async function payWithUpi(link) {
if (ads) ads.appOpen.setEnabled(false);
try {
await openPaymentApp(link); // your own code; the user switches apps and returns
} finally {
if (ads) ads.appOpen.setEnabled(true);
}
}Comprueba si la compilación tiene una unidad de apertura de la app:
var hasAppOpen = !!(window.AppMintAds && window.AppMintAds.config().formats.app_open);Ponlo en pausa mientras el usuario elige una foto, ya que el selector de fotos del sistema lo saca de la app por un momento:
var ads = window.AppMintAds;
var input = document.getElementById('photo-input'); // <input type="file" accept="image/*" hidden>
var pausedForPicker = false;
document.getElementById('pick-photo').addEventListener('click', function () {
if (ads) { ads.appOpen.setEnabled(false); pausedForPicker = true; }
input.click();
});
input.addEventListener('change', function () {
if (input.files[0]) uploadPhoto(input.files[0]); // your own code
});
// Back in the app (a photo was picked or the picker was cancelled): turn it on again.
document.addEventListener('visibilitychange', function () {
if (document.hidden || !pausedForPicker) return;
pausedForPicker = false;
setTimeout(function () { ads.appOpen.setEnabled(true); }, 1000);
});Notas: Está activado por defecto. En el arranque en frío el anuncio se muestra cuando la página está lista, o cuando termina de cargarse en 15 segundos; una carga posterior se guarda para la próxima vez. Un anuncio de apertura de la app de más de 4 horas nunca se muestra. También puedes mostrar uno tú mismo con AppMintAds.show('app_open').
AppMintAds.banner ayudante#
window.AppMintAds.banner.attach(target, opts) / .detach(slotOrElement)
Ejemplo
Coloca un banner o un anuncio de rectángulo mediano de 300×250 en cualquier parte de tu página, encima de un elemento que elijas.
Devuelve: attach devuelve una Promesa de {ok:true, slot, width, height} (px CSS) cuando el anuncio se ha cargado, o {ok:false, slot?, code, message?}. detach no devuelve nada. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio de banner (o rectángulo mediano).
El elemento solo reserva el espacio. El anuncio se dibuja encima y lo sigue cuando la página se desplaza o cambia de tamaño:
<div id="ad-slot"></div>
<script>
var ads = window.AppMintAds;
var slotEl = document.getElementById('ad-slot');
if (ads) {
ads.banner.attach(slotEl, { size: 'banner' }).then(function (res) {
// On success the helper sets slotEl.style.minHeight to res.height (pass keepHeight:true to stop that).
if (!res.ok) { // no_fill, network, too_many_slots, ...
if (res.slot) ads.banner.detach(res.slot); // a failed slot still counts toward the limit of two
slotEl.hidden = true;
}
});
} else {
slotEl.hidden = true; // browser: no ads here
}
</script>Rectángulo mediano (uno a la vez), y quitar de nuevo un banner:
var box = document.querySelector('.article-end-ad');
var mrecSlot = null;
if (window.AppMintAds) {
window.AppMintAds.banner.attach(box, { size: 'medium_rectangle' }).then(function (res) {
if (res.ok) mrecSlot = res.slot;
else { if (res.slot) window.AppMintAds.banner.detach(res.slot); box.hidden = true; }
});
}
function leaveArticle() {
if (window.AppMintAds && mrecSlot) window.AppMintAds.banner.detach(mrecSlot); // or detach(box)
mrecSlot = null;
}Vuelve a intentar un espacio más tarde cuando no había anuncio disponible, liberando primero el espacio fallido para que no consuma uno de los dos:
function fillSlot(el, triesLeft) {
var ads = window.AppMintAds;
if (!ads) { el.hidden = true; return; } // browser: no ads here
ads.banner.attach(el, { size: 'banner' }).then(function (res) {
if (res.ok) return;
if (res.slot) ads.banner.detach(res.slot);
if ((res.code === 'no_fill' || res.code === 'network') && triesLeft > 0) {
setTimeout(function () { fillSlot(el, triesLeft - 1); }, 60000);
} else {
el.hidden = true; // ad_free, disabled, consent, ...
}
});
}
fillSlot(document.getElementById('footer-ad'), 2);Notas: target es un elemento o un selector CSS; size es 'banner' (por defecto, ancho adaptable, como máximo 90 dp de alto), 'medium_rectangle' o 'mrec'. Como máximo dos espacios a la vez, y un espacio cuyo anuncio no se pudo cargar sigue contando hasta que lo desvincules. detach necesita el elemento o el ID de espacio de attach, no un selector. Quitar el elemento del DOM también lo desvincula. Códigos: no_element, duplicate_slot, too_many_slots, detached, no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode, internal.
AppMintAds.config ayudante#
window.AppMintAds.config()
Ejemplo
Le indica a la página qué modo de control de anuncios usa la compilación y qué formatos de anuncio puede servir.
Devuelve: un objeto, de forma síncrona: {control: 'automatic' | 'developer', formats: {banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}} (cada formato true / false). Ante un error interno devuelve {control:'automatic', formats:{}}. Requiere: nada; window.AppMintAds existe solo dentro de la app instalada.
Muestra solo la interfaz de anuncios que esta compilación realmente puede servir:
var ads = window.AppMintAds; // undefined in a browser
var cfg = ads ? ads.config() : null;
var developer = !!(cfg && cfg.control === 'developer');
document.getElementById('watch-ad').hidden = !(developer && cfg.formats.rewarded);
document.getElementById('ad-slot').hidden = !(developer && cfg.formats.banner);Elige el formato con recompensa que tiene la compilación para un botón de «bonificación», y oculta el botón cuando no tiene ninguno:
var ads = window.AppMintAds;
var cfg = ads ? ads.config() : null;
var bonusBtn = document.getElementById('bonus');
var bonusType = null;
if (cfg && cfg.control === 'developer') {
if (cfg.formats.rewarded) bonusType = 'rewarded';
else if (cfg.formats.rewarded_interstitial) bonusType = 'rewarded_interstitial';
}
bonusBtn.hidden = !bonusType;
bonusBtn.addEventListener('click', async function () {
var res = await ads.show(bonusType); // inside the tap: plays at once
if (res.earned) addCoins(res.reward.amount);
});Elige el tamaño de anuncio para el final de un artículo: un rectángulo mediano si la compilación lo tiene, y si no, un banner:
var ads = window.AppMintAds;
var cfg = ads ? ads.config() : null;
var box = document.getElementById('article-end-ad');
var size = null;
if (cfg && cfg.control === 'developer') {
size = cfg.formats.mrec ? 'medium_rectangle' : (cfg.formats.banner ? 'banner' : null);
}
if (!size) box.hidden = true;
else ads.banner.attach(box, { size: size }).then(function (res) {
if (!res.ok) { if (res.slot) ads.banner.detach(res.slot); box.hidden = true; }
});Notas: Un formato es true cuando los anuncios están activados y la compilación tiene un ID de unidad de anuncio para él. En modo automático (control: 'automatic') cada llamada a load / show / banner.attach responde code: 'automatic_mode'; usa WebToApk.showRewardedAd() / showInterstitialAd() ahí. Elige Control de anuncios → Controlado por el desarrollador en el asistente de compilación para manejar los anuncios desde la página.
AppMintAds.isReady ayudante#
window.AppMintAds.isReady(type)
Ejemplo
Te dice si un anuncio de pantalla completa de este tipo está cargado y puede mostrarse ahora mismo.
Devuelve: true / false, de forma síncrona. type: 'interstitial', 'rewarded', 'rewarded_interstitial' o 'app_open'. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para el tipo.
Mantén un botón «Ver anuncio» sincronizado con el anuncio, usando el evento de estado appmint:ad:
var ads = window.AppMintAds;
var btn = document.getElementById('watch-ad');
function refresh() { btn.disabled = !(ads && ads.isReady('rewarded')); }
if (ads) {
refresh();
window.addEventListener('appmint:ad', function (e) {
if (e.detail.type === 'rewarded') refresh(); // phases: loaded, failed, closed, ...
});
} else {
btn.hidden = true; // browser: no ads here
}Ofrece «duplica tus monedas» en la pantalla de resultados solo cuando ya hay un anuncio con recompensa esperando:
function showResults(coins) {
var ads = window.AppMintAds;
var offer = document.getElementById('double-coins');
renderResults(coins); // your own screen
offer.hidden = !(ads && ads.isReady('rewarded'));
offer.onclick = async function () {
offer.hidden = true;
var res = await ads.show('rewarded'); // inside the tap: plays at once
addCoins(res.earned ? coins : 0); // the bonus: the same amount again
};
}En un botón de pista, reproduce el formato con recompensa que esté cargado en este momento:
document.getElementById('hint').addEventListener('click', async function () {
var ads = window.AppMintAds;
if (!ads) return; // browser: no ads here
var type = ads.isReady('rewarded') ? 'rewarded'
: ads.isReady('rewarded_interstitial') ? 'rewarded_interstitial'
: null;
if (!type) { showMessage('No hint video right now. Try again in a minute.'); return; }
var res = await ads.show(type);
if (res.earned) revealHint();
});Notas: banner y mrec siempre responden false (se adjuntan, no se muestran). Un intersticial en su enfriamiento de 60 segundos responde false; un anuncio de apertura de la app de más de 4 horas responde false. En modo automático esto siempre es false.
AppMintAds.load ayudante#
window.AppMintAds.load(type)
Ejemplo
Carga un anuncio de pantalla completa para que un show(type) posterior pueda reproducirlo de inmediato.
Devuelve: una Promesa que se resuelve (nunca se rechaza) con {ok:true}, {ok:true, cached:true} cuando ya hay uno esperando, o {ok:false, code, reason, message?, adCode?}. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para el tipo (interstitial, rewarded, rewarded_interstitial, app_open).
var ads = window.AppMintAds;
var btn = document.getElementById('watch-ad');
btn.disabled = true;
async function prepareRewarded() {
if (!ads) { btn.hidden = true; return; } // browser: no ads
var res = await ads.load('rewarded');
if (res.ok) { btn.disabled = false; return; }
if (res.code === 'no_fill' || res.code === 'network') setTimeout(prepareRewarded, 30000);
else console.log('rewarded ad unavailable:', res.code, res.message);
}
prepareRewarded();Toda la vida de cada solicitud también se transmite en el evento appmint:ad:
window.addEventListener('appmint:ad', function (e) {
var a = e.detail; // {type, phase, slot?, code?, adCode?, message?, width?, height?, reward?, earned?}
// phase: loading, loaded, failed, opened, impression, clicked, closed, rewarded, show_failed, opt_in, declined
if (a.phase === 'failed') console.log(a.type, 'failed to load:', a.code, a.adCode, a.message);
});
// The same object also goes to window.onAppMintAd, if you define that function.
window.onAppMintAd = function (a) { console.log('ad', a.type, a.phase); };Carga el intersticial cuando empieza un nivel, para que esté listo cuando termine, y deja de pedirlo una vez que el usuario haya quitado los anuncios:
var ads = window.AppMintAds;
var interstitialsOff = false;
async function startLevel(n) {
runLevel(n); // your own game
if (!ads || interstitialsOff) return;
var res = await ads.load('interstitial');
if (!res.ok && (res.code === 'ad_free' || res.code === 'disabled' || res.code === 'automatic_mode')) {
interstitialsOff = true; // no interstitials in this app session
}
}
async function levelEnded() {
if (ads && !interstitialsOff && ads.isReady('interstitial')) await ads.show('interstitial');
showLevelSummary();
}Notas: Códigos: no_fill, network, invalid_unit, internal (de AdMob), automatic_mode, unknown_type, ad_free, disabled, consent y use_slot para banner / mrec (usa AppMintAds.banner.attach). La app ya precarga cada tipo de pantalla completa que tiene ID de unidad y carga el siguiente después de cada muestra, así que load sirve sobre todo para saber cuándo se puede habilitar el botón.
AppMintAds.show ayudante#
window.AppMintAds.show(type)
Ejemplo
Muestra un anuncio de pantalla completa cargado: interstitial, rewarded, rewarded_interstitial o app_open.
Devuelve: una Promesa que se resuelve (nunca se rechaza) cuando el anuncio se ha cerrado: {ok:true, shown:true, earned, reward?} (reward = {type, amount} para los formatos con recompensa), o {ok:false, shown:false, code, reason, message?} cuando no se mostró nada. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para el tipo.
Con recompensa: llámalo dentro del propio toque del usuario. Ese toque es la aceptación del usuario, así que el anuncio se reproduce de inmediato. Otorga la recompensa solo cuando earned sea verdadero:
var ads = window.AppMintAds;
document.getElementById('watch-ad').addEventListener('click', async function () {
if (!ads) return; // browser: no ads here
var res = await ads.show('rewarded');
if (res.earned) addCoins(res.reward.amount);
else if (res.reason === 'ad_free') addCoins(50); // user paid to remove ads: reward anyway
else if (res.shown) showMessage('Watch to the end to get the reward.');
else if (res.reason !== 'declined') showMessage('No ad available right now.');
});Intersticial: en una pausa natural. Continúa sea cual sea la respuesta:
async function levelFinished() {
if (window.AppMintAds && window.AppMintAds.isReady('interstitial')) {
await window.AppMintAds.show('interstitial'); // resolves when the ad closes
}
showNextLevel();
}Intersticial con recompensa detrás de tu propia pantalla de introducción con un claro «No, gracias». El toque en «Ver» es la aceptación:
var ads = window.AppMintAds;
var intro = document.getElementById('free-life-intro'); // your own dialog: Watch / No thanks
function offerFreeLife() {
if (ads && ads.isReady('rewarded_interstitial')) intro.hidden = false;
else gameOver();
}
document.getElementById('free-life-watch').addEventListener('click', async function () {
intro.hidden = true;
var res = await ads.show('rewarded_interstitial');
if (res.earned) continueWithExtraLife();
else gameOver();
});
document.getElementById('free-life-skip').addEventListener('click', function () {
intro.hidden = true;
gameOver();
});Notas: Un show() con recompensa o intersticial con recompensa que NO viene de un toque del usuario (un temporizador, la carga de la página, un cambio de ruta) primero muestra una pantalla «¿Ver un anuncio? / No, gracias», y «No, gracias» responde declined. Nunca actives formatos con recompensa de esa manera. Otros códigos: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (60 s entre intersticiales), not_loaded (se inicia una nueva carga para la próxima vez), show_failed, internal. En modo automático usa WebToApk.showRewardedAd().
adsAppOpenEnable puente#
window.WebToApk.adsAppOpenEnable(enabled: Boolean)
Permite que la página pause/reanude el anuncio de apertura de la app al volver de otra app (activado por defecto cuando hay una unidad).
Ejemplo
La mitad nativa de AppMintAds.appOpen.setEnabled(on): pausa o reanuda el anuncio de apertura de la app. Las páginas usan AppMintAds.appOpen.setEnabled.
Devuelve: nada. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio de apertura de la app.
function setAppOpenAds(on) {
if (window.AppMintAds) window.AppMintAds.appOpen.setEnabled(on);
}
// Pause while the user is in the middle of something, resume afterwards.
function startCheckout() { setAppOpenAds(false); showCheckout(); }
function checkoutDone() { setAppOpenAds(true); }Elegir una foto sale de la app hacia el selector del sistema, y al volver contaría como un regreso. Pausa el anuncio de apertura de la app a su alrededor:
var input = document.getElementById('photo');
function appOpenAds(on) {
if (window.AppMintAds) window.AppMintAds.appOpen.setEnabled(on);
}
input.addEventListener('click', function () { appOpenAds(false); });
function pickerDone() { setTimeout(function () { appOpenAds(true); }, 1000); }
input.addEventListener('change', pickerDone);
input.addEventListener('cancel', pickerDone);Los miembros de tu propio plan de pago nunca lo ven. El ajuste no se guarda, así que defínelo temprano en cada inicio:
// Runs from a <head> script: window.AppMintAds is already there at document start.
var plan = null;
try { plan = localStorage.getItem('plan'); } catch (e) {}
if (window.AppMintAds && plan === 'pro') {
window.AppMintAds.appOpen.setEnabled(false);
}Notas: Firma directa adsAppOpenEnable(enabled). Activado por defecto cuando hay una unidad de apertura de la app. El anuncio de apertura se muestra en el arranque en frío (si se carga en 15 segundos) y cuando el usuario vuelve de otra app; un anuncio de más de 4 horas nunca se muestra.
adsConfig puente#
window.WebToApk.adsConfig(): String
`{control, formats:{banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}}`: los formatos de anuncio que esta compilación puede servir.
Ejemplo
Lee qué formatos de anuncio puede servir esta compilación y qué modo de control de anuncios usa. Las páginas normalmente llaman a AppMintAds.config(), que lo interpreta por ti.
Devuelve: una cadena JSON: {"control":"automatic"|"developer","formats":{"banner":bool,"mrec":bool,"interstitial":bool,"rewarded":bool,"rewarded_interstitial":bool,"app_open":bool}}. Un formato es true cuando los anuncios están activados y la compilación tiene un ID de unidad de anuncio para él. Requiere: nada.
Primero la API pública:
if (window.AppMintAds) {
var cfg = window.AppMintAds.config(); // already parsed; {control:'automatic', formats:{}} on error
if (cfg.control === 'developer' && cfg.formats.rewarded) {
document.getElementById('watch-ad').hidden = false;
}
}La llamada directa, si no usas AppMintAds:
var b = window.WebToApk;
if (b && typeof b.adsConfig === 'function') {
try {
var cfg = JSON.parse(b.adsConfig());
console.log(cfg.control, cfg.formats);
} catch (e) { console.log('could not read ad config', e); }
}Reserva espacio solo para los formatos que esta compilación realmente tiene, y devuelve a la página la caja vacía en caso contrario:
var box = document.getElementById('sidebar-ad');
var cfg = window.AppMintAds ? window.AppMintAds.config() : { control: 'automatic', formats: {} };
if (cfg.control === 'developer' && cfg.formats.mrec) {
window.AppMintAds.banner.attach(box, { size: 'mrec' });
} else if (cfg.control === 'developer' && cfg.formats.banner) {
window.AppMintAds.banner.attach(box, { size: 'banner' });
} else {
box.remove(); // browser, Automatic mode, or no unit: no empty gap
}Notas: En modo automático formats sigue diciéndote qué unidades existen, pero AppMintAds.load/show responden code: 'automatic_mode'; usa WebToApk.showRewardedAd() / showInterstitialAd() ahí.
adsIsReady puente#
window.WebToApk.adsIsReady(type: String): Boolean
Verdadero cuando un [type] de pantalla completa está cargado y puede mostrarse ahora mismo.
Ejemplo
Te dice si un anuncio de pantalla completa de un tipo está cargado y puede mostrarse ahora (anuncios controlados por el desarrollador). Las páginas normalmente llaman a AppMintAds.isReady(type).
Devuelve: true / false, de forma síncrona. type es 'interstitial', 'rewarded', 'rewarded_interstitial' o 'app_open'; 'banner' / 'mrec' siempre responden false. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para ese tipo.
Primero la API pública:
var watchBtn = document.getElementById('watch-ad');
function refresh() {
watchBtn.disabled = !(window.AppMintAds && window.AppMintAds.isReady('rewarded'));
}
refresh();
window.addEventListener('appmint:ad', refresh); // loaded / closed / failed all change readinessLa llamada en bruto:
var b = window.WebToApk;
var ready = !!(b && typeof b.adsIsReady === 'function' && b.adsIsReady('interstitial'));Muestra un intersticial al final de un nivel solo cuando haya uno esperando, y nunca detengas al jugador cuando no lo haya:
function onLevelComplete() {
var ads = window.AppMintAds;
if (ads && ads.isReady('interstitial')) {
// resolves after the ad closes, or at once with {ok:false, code} if it could not show
ads.show('interstitial').then(startNextLevel);
} else {
startNextLevel(); // not loaded yet, or inside the 60-second cooldown
}
}Notas: Un intersticial dentro de su enfriamiento de 60 segundos responde false. Un anuncio de apertura de la app de más de 4 horas responde false (Google indica que no se muestre). En modo automático esto siempre es false; usa WebToApk.isRewardedAdReady() ahí.
adsLoad puente#
window.WebToApk.adsLoad(type: String, callbackId: String)
Carga un [type] de pantalla completa; la promesa de [callbackId] se resuelve con el resultado.
Ejemplo
La mitad nativa de AppMintAds.load(type): empieza a cargar un anuncio de pantalla completa y responde al callback del shim. Las páginas usan AppMintAds.load(type), que devuelve una Promesa; no llames a esto directamente.
Devuelve: (a través de AppMintAds.load) una Promesa de {ok:true}, {ok:true, cached:true} cuando ya hay uno cargado, o {ok:false, code, reason, message?, adCode?}. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para el tipo.
var watchBtn = document.getElementById('watch-ad');
watchBtn.disabled = true;
if (window.AppMintAds) {
window.AppMintAds.load('rewarded').then(function (res) {
if (res.ok) watchBtn.disabled = false;
else console.log('rewarded ad not loaded:', res.code, res.message);
});
} else {
watchBtn.hidden = true; // browser: there are no ads here
}Reintenta una carga que falló por un motivo pasajero y detente ante los motivos que un reintento no puede resolver:
function loadWithRetry(type, attempt) {
attempt = attempt || 0;
return window.AppMintAds.load(type).then(function (res) {
if (res.ok) return res;
if ((res.code === 'no_fill' || res.code === 'network') && attempt < 3) {
return new Promise(function (wait) { setTimeout(wait, 5000 * (attempt + 1)); })
.then(function () { return loadWithRetry(type, attempt + 1); });
}
return res; // invalid_unit, consent, ad_free, disabled ...
});
}
if (window.AppMintAds) {
loadWithRetry('rewarded').then(function (res) {
document.getElementById('watch-ad').disabled = !res.ok;
});
}Precalienta dos formatos antes de que empiece una sesión de juego y oculta todos los botones de anuncio cuando el usuario no tiene anuncios:
if (window.AppMintAds) {
Promise.all([
window.AppMintAds.load('interstitial'),
window.AppMintAds.load('rewarded_interstitial')
]).then(function (results) {
var noAds = results.some(function (r) { return r.code === 'ad_free' || r.code === 'disabled'; });
if (noAds) hideAllAdButtons(); // paid to remove ads, or ads are off in this build
results.forEach(function (r) {
if (!r.ok && r.adCode !== undefined) console.log('ad load failed', r.code, r.adCode, r.message);
});
});
}Notas: Códigos de fallo: no_fill, network, invalid_unit, internal (de AdMob, con adCode), y automatic_mode, unknown_type, ad_free, disabled, consent y use_slot para banner / mrec (esos se cargan con AppMintAds.banner.attach). La app ya precarga cada tipo de pantalla completa que tiene ID de unidad y recarga después de cada muestra. La firma directa es adsLoad(type, callbackId); su respuesta va a un resolvedor interno, así que una llamada directa no te da resultado.
adsShow puente#
window.WebToApk.adsShow(type: String, callbackId: String, userGesture: Boolean)
Muestra un [type] de pantalla completa cargado; se resuelve cuando se cierra, con earned/reward para formatos con recompensa. [userGesture] es la lectura de `navigator.userActivation` del shim: un formato con recompensa solicitado sin ella recibe primero la pantalla de introducción de AdMob (ver DeveloperAds.offerRewarded).
Ejemplo
La mitad nativa de AppMintAds.show(type): muestra un anuncio de pantalla completa cargado y responde cuando se cierra. Las páginas usan AppMintAds.show(type); no llames a esto directamente.
Devuelve: (a través de AppMintAds.show) una Promesa de {ok:true, shown:true, earned, reward?} después de que se cierre el anuncio (reward = {type, amount} para formatos con recompensa), o {ok:false, shown:false, code, reason, message?}. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio para el tipo.
Llámalo desde el toque del usuario. El shim pasa navigator.userActivation.isActive como tercer argumento (userGesture):
document.getElementById('watch-ad').addEventListener('click', function () {
if (!window.AppMintAds) return;
window.AppMintAds.show('rewarded').then(function (res) {
if (res.earned) addCoins(res.reward.amount);
else if (res.reason === 'ad_free') addCoins(50); // paid to remove ads: reward anyway
else if (res.reason !== 'declined') showMessage('No ad available right now.');
});
});Ofrece un intersticial con recompensa desde el propio toque del usuario en «Duplicar mi bonificación», y continúa sea cual sea la respuesta:
document.getElementById('double-bonus').addEventListener('click', function () {
if (!window.AppMintAds) return goToNextLevel();
window.AppMintAds.show('rewarded_interstitial').then(function (res) {
if (res.earned) doubleLevelBonus();
else if (!res.shown && res.code !== 'declined') console.log('no ad:', res.code);
goToNextLevel(); // shown or not, the player keeps going
});
});Pausa la música de tu juego mientras cualquier anuncio de pantalla completa esté en pantalla, usando el flujo appmint:ad:
window.addEventListener('appmint:ad', function (e) {
var d = e.detail; // {type, phase, code?, adCode?, message?, earned?, reward?}
if (d.type === 'banner' || d.type === 'mrec') return;
if (d.phase === 'opened') music.pause();
if (d.phase === 'closed' || d.phase === 'show_failed') music.play();
});
function onChapterSaved() {
if (window.AppMintAds) window.AppMintAds.show('interstitial'); // a natural break
}Notas: Firma directa adsShow(type, callbackId, userGesture). Cuando userGesture es false para rewarded o rewarded_interstitial, la app primero pregunta «¿Ver un anuncio? / No, gracias» y un «no» responde declined. Otros códigos: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (intersticial, 60 s), not_loaded (se inicia una carga para la próxima vez), show_failed, internal.
adsSlotAttach puente#
window.WebToApk.adsSlotAttach(slotId: String, type: String, widthCss: Int, callbackId: String)
Crea una superposición de banner/mrec para el espacio [slotId] de la página, con el tamaño [widthCss]; se resuelve con el tamaño del anuncio una vez cargado.
Ejemplo
La mitad nativa de AppMintAds.banner.attach(element, {size}): crea un anuncio de banner o rectángulo mediano sobre un elemento de la página. Las páginas usan AppMintAds.banner.attach; no llames a esto directamente.
Devuelve: (a través de AppMintAds.banner.attach) una Promesa de {ok:true, slot, width, height} en píxeles CSS cuando el anuncio se ha cargado, o {ok:false, slot, code, message?}. Requiere: Control de anuncios → Controlado por el desarrollador y un ID de unidad de anuncio de banner (o rectángulo mediano).
<div id="ad-slot"></div>
<script>
if (window.AppMintAds) {
window.AppMintAds.banner.attach('#ad-slot', { size: 'banner' }).then(function (res) {
if (!res.ok) console.log('no banner:', res.code); // e.g. no_fill, network, too_many_slots
});
}
</script>Un rectángulo mediano en una pantalla de resultados que devuelve su espacio cuando no llega ningún anuncio:
var box = document.getElementById('result-ad');
if (window.AppMintAds) {
window.AppMintAds.banner.attach(box, { size: 'mrec' }).then(function (res) {
if (res.ok) return; // the box now has min-height = res.height
window.AppMintAds.banner.detach(res.slot); // free the slot for another banner
box.hidden = true;
if (res.code === 'too_many_slots') console.log('detach a banner first:', res.message);
});
} else {
box.hidden = true;
}Cuenta impresiones y clics por espacio a partir del flujo appmint:ad, usando como clave el ID de espacio attach que devolvió:
var slotNames = {};
if (window.AppMintAds) {
window.AppMintAds.banner.attach('#footer-ad', { size: 'banner' }).then(function (res) {
if (res.ok) slotNames[res.slot] = 'footer';
});
}
window.addEventListener('appmint:ad', function (e) {
var d = e.detail; // banner events carry {type, phase, slot}
if (d.slot && (d.phase === 'impression' || d.phase === 'clicked')) {
track('ad_' + d.phase, slotNames[d.slot] || d.slot);
}
});Notas: Firma directa adsSlotAttach(slotId, type, widthCss, callbackId); type es 'banner' o 'mrec'. Un WebView no puede contener una vista nativa dentro de la página, así que el anuncio se dibuja sobre tu elemento y lo sigue al desplazarse y redimensionarse (ver adsSlotRect). Como máximo dos espacios a la vez, y solo un rectángulo mediano. Códigos adicionales: duplicate_slot, too_many_slots, detached, internal, más los códigos de carga (no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode).
adsSlotDetach puente#
window.WebToApk.adsSlotDetach(slotId: String)
Elimina una superposición de banner.
Ejemplo
La mitad nativa de AppMintAds.banner.detach(slotOrElement): elimina una superposición de banner y libera su espacio. Las páginas usan AppMintAds.banner.detach.
Devuelve: nada. Requiere: Control de anuncios → Controlado por el desarrollador.
Quita el banner antes de una pantalla donde un anuncio no corresponde (un reproductor de video, un pago):
var slotEl = document.getElementById('ad-slot');
function openPlayer() {
if (window.AppMintAds) window.AppMintAds.banner.detach(slotEl); // the element, or the slot id from attach()
showPlayer();
}
function closePlayer() {
hidePlayer();
if (window.AppMintAds) window.AppMintAds.banner.attach(slotEl, { size: 'banner' });
}En una app de una sola página, libera el banner cuando desaparece la ruta que lo contiene. attach marca el elemento de inmediato, así que desvincular por elemento funciona incluso mientras el anuncio aún se está cargando:
function onRouteChange(route) {
if (!window.AppMintAds) return renderRoute(route);
var old = document.getElementById('article-ad');
if (old) window.AppMintAds.banner.detach(old);
renderRoute(route);
if (route === '/articles') {
window.AppMintAds.banner.attach('#article-ad', { size: 'banner' });
}
}Tras una compra de «Quitar anuncios» las superposiciones desaparecen; desvincula también los espacios y elimina el espacio que reservaban:
window.addEventListener('appmint:premium', function (e) {
if (!e.detail.premium || !window.AppMintAds) return;
document.querySelectorAll('[data-appmint-ad-slot]').forEach(function (el) {
window.AppMintAds.banner.detach(el);
el.remove();
});
});Notas: Firma directa adsSlotDetach(slotId). detach recibe el elemento o la cadena slot que devolvió attach, no un selector CSS. Una Promesa de attach que aún espera su anuncio responde {ok:false, code:'detached'}. Quitar el elemento del DOM también desvincula el espacio.
adsSlotRect puente#
window.WebToApk.adsSlotRect(slotId: String, x: Float, y: Float, w: Float, h: Float, visible: Boolean)
Interno: el rectángulo actual del viewport del elemento del espacio (px CSS); mueve la superposición.
Ejemplo
Interno: mueve una superposición de banner a la posición actual de su elemento. El asistente AppMintAds.banner lo llama por ti al desplazarse, redimensionar, cambiar la visibilidad y cada 500 ms; las páginas usan AppMintAds.banner.attach y nunca llaman a esto.
Devuelve: nada. Requiere: Control de anuncios → Controlado por el desarrollador y un espacio creado por AppMintAds.banner.attach.
Lo único que hace una página es darle al elemento del espacio un lugar en el diseño. El asistente lo mide y mantiene el anuncio encima:
<main>
<article id="story">…</article>
<div id="ad-slot" style="margin: 16px 0"></div> <!-- the ad follows this box while scrolling -->
</main>
<script>
if (window.AppMintAds) window.AppMintAds.banner.attach('#ad-slot', { size: 'banner' });
</script>Un banner dentro de una lista con desplazamiento, no de la página: el asistente escucha el scroll en la fase de captura, así que el anuncio también sigue al contenedor interno:
<div id="feed" style="height: 100vh; overflow-y: auto">
<div class="card">…</div>
<div class="card">…</div>
<div id="feed-ad" style="margin: 12px 0"></div>
<div class="card">…</div>
</div>
<script>
if (window.AppMintAds) window.AppMintAds.banner.attach('#feed-ad', { size: 'banner' });
</script>El anuncio se dibuja encima de la página, así que tu propio diálogo quedaría debajo. Oculta el espacio mientras el diálogo esté abierto:
var slotEl = document.getElementById('ad-slot');
var dialog = document.getElementById('settings-dialog');
function openSettings() {
slotEl.style.visibility = 'hidden'; // the helper hides the overlay on its next tick
dialog.showModal();
}
dialog.addEventListener('close', function () {
slotEl.style.visibility = ''; // and puts it back over the box
});Notas: Firma directa adsSlotRect(slotId, x, y, w, h, visible), en píxeles CSS relativos al viewport. La superposición se oculta mientras el elemento está fuera de pantalla, tiene tamaño cero, tiene visibility: hidden o la página está en segundo plano. Si el elemento sale del DOM, el asistente desvincula el espacio por sí mismo.
isInterstitialAdReady puente#
window.WebToApk.isInterstitialAdReady(): Boolean
Verdadero cuando un intersticial está cargado Y ya pasó su enfriamiento (coincide exactamente con lo que showInterstitialAd() aceptará; no hay un «listo» que luego se niegue a mostrarse).
Ejemplo
Te dice si un anuncio intersticial está cargado Y ya pasó su enfriamiento de 60 segundos, de modo que showInterstitialAd() lo mostraría ahora (control de anuncios automático).
Devuelve: true / false, de forma síncrona. Requiere: anuncios activados y Anuncio de pantalla completa → Anuncio intersticial.
function onLevelDone() {
var b = window.WebToApk;
if (b && typeof b.isInterstitialAdReady === 'function' && b.isInterstitialAdReady()) {
b.showInterstitialAd(); // wait for 'appmint:ad-closed' before continuing
} else {
startNextLevel(); // nothing ready: never block the user
}
}
window.addEventListener('appmint:ad-closed', function (e) {
if (e.detail.adType === 'interstitial') startNextLevel();
});Muestra un anuncio después de cada tercer cuestionario terminado, y solo cuando realmente se vaya a mostrar uno:
var finished = 0;
function onQuizFinished() {
finished++;
var b = window.WebToApk;
var ready = !!(b && typeof b.isInterstitialAdReady === 'function' && b.isInterstitialAdReady());
if (finished % 3 === 0 && ready) b.showInterstitialAd();
showResults(); // the results are there when the ad closes
}Muestra un breve aviso «Anuncio en 3…» solo cuando haya un anuncio listo, y luego continúa cuando se cierre o no pueda mostrarse:
var afterAd = null;
function adBreakThen(next) {
var b = window.WebToApk;
if (!b || typeof b.isInterstitialAdReady !== 'function' || !b.isInterstitialAdReady()) return next();
var note = document.getElementById('ad-break'), n = 3;
note.hidden = false; note.textContent = 'Ad in 3…';
var t = setInterval(function () {
if (--n > 0) { note.textContent = 'Ad in ' + n + '…'; return; }
clearInterval(t); note.hidden = true;
afterAd = next;
b.showInterstitialAd();
}, 1000);
}
function runAfterAd(e) {
if (e.detail.adType !== 'interstitial' || !afterAd) return;
var next = afterAd; afterAd = null; next();
}
window.addEventListener('appmint:ad-closed', runAfterAd);
window.addEventListener('appmint:ad-unavailable', runAfterAd);Notas: true aquí significa que la muestra no será rechazada por not_loaded ni cooldown. El consentimiento o una compra de «Quitar anuncios» aún pueden impedirla (appmint:ad-unavailable). En compilaciones controladas por el desarrollador usa AppMintAds.isReady('interstitial').
isRewardedAdReady puente#
window.WebToApk.isRewardedAdReady(): Boolean
Verdadero cuando un anuncio con recompensa está cargado y puede mostrarse ahora mismo. Deja que la página habilite/deshabilite su botón «Ver anuncio» según esto (consúltalo periódicamente o antes de mostrar).
Ejemplo
Te dice si un anuncio con recompensa está cargado y puede mostrarse ahora mismo (control de anuncios automático).
Devuelve: true / false, de forma síncrona. Requiere: anuncios activados y Anuncio de pantalla completa → Anuncio con recompensa.
Habilita el botón «Ver un anuncio» solo cuando haya un anuncio esperando:
var btn = document.getElementById('earn');
function refreshWatchButton() {
var b = window.WebToApk;
var ready = !!(b && typeof b.isRewardedAdReady === 'function' && b.isRewardedAdReady());
btn.disabled = !ready;
btn.textContent = ready ? 'Watch an ad for 10 coins' : 'Loading ad…';
}
refreshWatchButton();
setInterval(refreshWatchButton, 2000); // an ad can finish loading at any time
window.addEventListener('appmint:ad-closed', refreshWatchButton);En el fin del juego, ofrece «Continuar» solo cuando haya un anuncio esperando, para que el botón nunca lleve a ninguna parte:
var continueBtn = document.getElementById('continue-with-ad');
function onGameOver() {
var b = window.WebToApk;
continueBtn.hidden = !(b && typeof b.isRewardedAdReady === 'function' && b.isRewardedAdReady());
showGameOverPanel(); // "Restart" is always there
}
continueBtn.addEventListener('click', function () { // the user's own tap
continueBtn.hidden = true;
window.WebToApk.showRewardedAd();
});
window.addEventListener('appmint:reward', function () { revivePlayer(); });Comprueba en el momento del toque y dile al usuario qué está pasando, en lugar de lanzar una muestra que será rechazada:
document.getElementById('hint').addEventListener('click', function () {
var b = window.WebToApk;
if (!b || typeof b.isRewardedAdReady !== 'function') return showMessage('Hints need the app.');
if (typeof b.isPremium === 'function' && b.isPremium()) return revealHint(); // paid to remove ads
if (b.isRewardedAdReady()) b.showRewardedAd();
else showMessage('The ad is still loading. Try again in a few seconds.');
});
window.addEventListener('appmint:reward', function () { revealHint(); });Notas: Devuelve false en un navegador, cuando los anuncios se quitaron por una compra y antes de que se cargue el primer anuncio. Después de mostrar un anuncio, el siguiente se carga solo. En compilaciones controladas por el desarrollador usa AppMintAds.isReady('rewarded').
notifyUserAction puente#
window.WebToApk.notifyUserAction()
Interno (lo llama el contador de toques inyectado, no el código del creador): ocurrió una interacción significativa del usuario en la página. Cuenta para la cadencia de «intersticial cada N acciones» SOLO mientras nunca se haya observado una navegación basada en URL: las apps de un solo HTML que cambian de pantalla mostrando/ocultando DOM nunca cambian la URL, así que sin esto su contador de navegación se quedaba en 0 para siempre y los anuncios configurados nunca se mostraban (clase de queja TicTrek, 2026-07-26). Las apps de URL/SPA no se ven afectadas: su primera navegación real vuelve a cambiar el conteo permanentemente a las señales de URL.
Ejemplo
Cuenta una acción significativa del usuario para el ajuste «anuncio de pantalla completa cada N acciones». La app lo llama por sí misma desde su propio contador de toques; las páginas normalmente nunca lo llaman.
Devuelve: nada. Requiere: anuncios activados, un anuncio de pantalla completa con Modo de activación → Después de navegaciones y control de anuncios automático.
Cómo funciona: una página que cambia de pantalla ocultando y mostrando elementos del DOM nunca cambia su URL, así que la app no puede ver una «navegación». Por eso la app inyecta un contador de toques (como máximo un conteo cada 2 segundos) que llama a este método. En cuanto la app ve un cambio real de URL (una carga de página, pushState o un #hash), cuenta los cambios de URL en su lugar y este método deja de contar.
Solo si los cambios de pantalla no provienen de un toque (por ejemplo un movimiento de teclado o de gamepad), repórtalos tú mismo:
function goToScreen(name) {
showScreen(name); // your own DOM show/hide
var b = window.WebToApk;
if (b && typeof b.notifyUserAction === 'function') b.notifyUserAction();
}
document.addEventListener('keydown', function (e) {
if (e.key === 'Enter') goToScreen('next');
});Una app estilo tarjetas donde el usuario desliza a la siguiente pantalla (deslizar no es tocar un botón, así que el contador de toques nunca lo ve):
var startX = null;
document.addEventListener('touchstart', function (e) { startX = e.touches[0].clientX; }, { passive: true });
document.addEventListener('touchend', function (e) {
if (startX === null) return;
var dx = e.changedTouches[0].clientX - startX;
startX = null;
if (Math.abs(dx) < 80) return; // not a swipe
showScreen(dx < 0 ? 'next' : 'previous'); // your own DOM show/hide
var b = window.WebToApk;
if (b && typeof b.notifyUserAction === 'function') b.notifyUserAction();
});Un juego HTML5 jugado con un gamepad, contando una acción por cada nivel que el jugador inicia con el botón A:
var aWasDown = false;
function pollPad() {
var pad = navigator.getGamepads ? navigator.getGamepads()[0] : null;
var aDown = !!(pad && pad.buttons[0] && pad.buttons[0].pressed);
if (aDown && !aWasDown && isOnLevelSelect()) { // your own game state
startSelectedLevel();
var b = window.WebToApk;
if (b && typeof b.notifyUserAction === 'function') b.notifyUserAction();
}
aWasDown = aDown;
requestAnimationFrame(pollPad);
}
window.addEventListener('gamepadconnected', function () { requestAnimationFrame(pollPad); }, { once: true });Notas: No hace nada cuando los anuncios están desactivados, eliminados por una compra, o cuando la compilación usa Control de anuncios → Controlado por el desarrollador (ahí llamas tú mismo a AppMintAds.show()). Un anuncio con recompensa que se alcanza de esta forma siempre se ofrece con la pregunta «¿Ver un anuncio? / No, gracias», nunca se impone. No lo llames en cada toque; mostraría anuncios con más frecuencia de la que configuraste.
showInterstitialAd puente#
window.WebToApk.showInterstitialAd()
Muestra un intersticial AHORA (p. ej. entre niveles / en una acción deliberada). Si no se puede mostrar ahora mismo → se omite + 'appmint:ad-unavailable' con el motivo.
Ejemplo
Muestra un anuncio intersticial de pantalla completa ahora mismo, por ejemplo entre dos niveles de un juego (control de anuncios automático).
Devuelve: nada. Cuando el anuncio se cierra, se dispara appmint:ad-closed con {adType:'interstitial', earned:false}. Cuando no puede mostrarse, se dispara appmint:ad-unavailable con {adType:'interstitial', reason}. Requiere: anuncios activados y Anuncio de pantalla completa → Anuncio intersticial. Elige Modo de activación → Bajo demanda si solo tu página debe decidir cuándo.
var bridge = window.WebToApk;
var waitingForAd = false;
function levelFinished() {
if (bridge && typeof bridge.isInterstitialAdReady === 'function' && bridge.isInterstitialAdReady()) {
waitingForAd = true;
bridge.showInterstitialAd(); // the next level starts when the ad closes
} else {
showNextLevel(); // no ad ready: just continue
}
}
window.addEventListener('appmint:ad-closed', function (e) {
if (e.detail.adType === 'interstitial' && waitingForAd) { waitingForAd = false; showNextLevel(); }
});
window.addEventListener('appmint:ad-unavailable', function (e) {
// reason: ad_free | disabled | consent | not_loaded | cooldown | show_failed
if (e.detail.adType === 'interstitial' && waitingForAd) { waitingForAd = false; showNextLevel(); }
});Pausa el juego y su sonido antes del anuncio, y reanuda con cualquiera de las dos respuestas:
var pausedForAd = false;
function adBetweenRounds() {
var b = window.WebToApk;
if (!b || typeof b.showInterstitialAd !== 'function') return;
pausedForAd = true;
pauseGame(); music.pause();
b.showInterstitialAd();
}
function resumeAfterAd(e) {
if (e.detail.adType !== 'interstitial' || !pausedForAd) return;
pausedForAd = false;
music.play(); resumeGame();
}
window.addEventListener('appmint:ad-closed', resumeAfterAd);
window.addEventListener('appmint:ad-unavailable', resumeAfterAd);Abre un artículo después de un anuncio, como una sola Promesa que se resuelve true cuando se mostró el anuncio y false cuando se omitió:
function interstitialThen() {
return new Promise(function (resolve) {
var b = window.WebToApk;
if (!b || typeof b.isInterstitialAdReady !== 'function' || !b.isInterstitialAdReady()) return resolve(false);
function done(e) {
if (e.detail.adType !== 'interstitial') return;
window.removeEventListener('appmint:ad-closed', done);
window.removeEventListener('appmint:ad-unavailable', done);
resolve(e.type === 'appmint:ad-closed');
}
window.addEventListener('appmint:ad-closed', done);
window.addEventListener('appmint:ad-unavailable', done);
b.showInterstitialAd();
});
}
interstitialThen().then(function () { openArticle(articleId); });Notas: La app mantiene al menos 60 segundos entre dos intersticiales; una llamada dentro de ese intervalo responde cooldown. isInterstitialAdReady() ya incluye el enfriamiento, así que comprobarlo primero evita el evento. Muestra los intersticiales en pausas naturales, nunca en cada toque. En compilaciones controladas por el desarrollador usa AppMintAds.show('interstitial').
showRewardedAd puente#
window.WebToApk.showRewardedAd()
Muestra un anuncio con recompensa AHORA (iniciado por el usuario: la única forma que cumple las políticas de mostrar anuncios con recompensa). Combínalo con el modo de activación «Bajo demanda» en el asistente para tener anuncios con recompensa SIN ningún anuncio automático de apertura/navegación. La recompensa se entrega de vuelta a la página mediante deliverRewardToPage(): window.addEventListener('appmint:reward', e => { /* e.detail = {type,amount} */ }) Cuando el anuncio se cierra, se dispara 'appmint:ad-closed' con {adType:'rewarded', earned}; earned es false si el usuario salió antes de tiempo, así que en ese caso no otorgues nada. Si no se puede mostrar ahora mismo → se omite y se dispara 'appmint:ad-unavailable' con el motivo.
Ejemplo
Muestra un anuncio con recompensa ahora mismo, desde el propio toque del usuario en «Ver un anuncio» (control de anuncios automático).
Devuelve: nada. El resultado llega después como eventos: appmint:reward {type, amount} cuando el anuncio se vio hasta el final, appmint:ad-closed {adType:'rewarded', earned} cuando se cierra, o appmint:ad-unavailable {adType:'rewarded', reason} cuando no se pudo mostrar nada. Requiere: anuncios activados, Anuncio de pantalla completa → Anuncio con recompensa y Modo de activación → Bajo demanda para que nada se muestre por sí solo.
Otorga la recompensa solo en el manejador de recompensa, nunca al tocar:
var btn = document.getElementById('earn');
var bridge = window.WebToApk;
function refresh() {
btn.disabled = !(bridge && typeof bridge.isRewardedAdReady === 'function' && bridge.isRewardedAdReady());
}
refresh();
setInterval(refresh, 2000);
btn.addEventListener('click', function () { // a real user tap: the only place to call it
if (!bridge || typeof bridge.showRewardedAd !== 'function') return;
btn.disabled = true;
bridge.showRewardedAd();
});
// GRANT HERE ONLY: the ad was watched to the end.
window.addEventListener('appmint:reward', function (e) {
addCoins(e.detail.amount); // e.detail = {type, amount}
});
// The ad closed. earned === false means the user left early: grant nothing.
window.addEventListener('appmint:ad-closed', function (e) {
if (e.detail.adType === 'rewarded' && !e.detail.earned) showMessage('No reward: the ad was closed early.');
refresh();
});
// Nothing could be shown.
window.addEventListener('appmint:ad-unavailable', function (e) {
// e.detail = {adType, reason}; reason: ad_free | disabled | consent | not_loaded | show_failed
if (e.detail.reason === 'ad_free') addCoins(1); // the user paid to remove ads: reward anyway
else showMessage('No ad right now. Try again in a moment.');
refresh();
});Los mismos tres momentos también llaman a ganchos de la página, si prefieres una función para cada uno (cada una recibe el mismo objeto que e.detail):
window.onAppMintReward = function (reward) { addCoins(reward.amount); }; // {type, amount}
window.onAppMintAdClosed = function (info) { console.log(info.adType, info.earned); };
window.onAppMintAdUnavailable = function (info) { console.log(info.adType, info.reason); };Envuelve la llamada y sus eventos en una sola Promesa, para que cada botón «Ver un anuncio» se lea como una llamada asíncrona normal:
function watchRewardedAd() { // call it inside the user's tap
return new Promise(function (resolve) {
var b = window.WebToApk;
if (!b || typeof b.showRewardedAd !== 'function') return resolve({ earned: false, reason: 'no_app' });
function done(e) {
if (e.detail.adType !== 'rewarded') return;
window.removeEventListener('appmint:ad-closed', done);
window.removeEventListener('appmint:ad-unavailable', done);
resolve(e.type === 'appmint:ad-closed' ? { earned: e.detail.earned } : { earned: false, reason: e.detail.reason });
}
window.addEventListener('appmint:ad-closed', done);
window.addEventListener('appmint:ad-unavailable', done);
b.showRewardedAd();
});
}
document.getElementById('revive').addEventListener('click', function () {
watchRewardedAd().then(function (r) {
if (r.earned || r.reason === 'ad_free') revivePlayer();
else if (r.reason) showMessage('No ad right now.');
});
});Notas: Llámalo solo desde un manejador de clic. Nunca lo llames desde un temporizador, la carga de la página o un cambio de ruta, y nunca pongas una función esencial detrás de él. not_loaded también inicia una nueva carga, así que el siguiente toque normalmente funciona. En compilaciones con Control de anuncios → Controlado por el desarrollador usa en su lugar AppMintAds.show('rewarded'), que responde con una Promesa.