API da ponte JavaScript
Anúncios
Exibir um anúncio intersticial ou com recompensa no momento que você escolher e avisar ao shell quando uma navegação começa e termina para que ele não interrompa uma.
AppMintAds.appOpen helper#
window.AppMintAds.appOpen.setEnabled(on)
Exemplo
Pausa ou retoma o anúncio de abertura do app, o anúncio em tela cheia exibido ao iniciar e quando o usuário volta de outro app.
Retorna: nada. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio de abertura do app.
Pause-o enquanto o usuário sai do app de propósito por um instante (um app de pagamento, a câmera, uma folha de compartilhamento), para que ao voltar não apareça um anúncio:
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);
}
}Verifique se o build tem uma unidade de abertura do app:
var hasAppOpen = !!(window.AppMintAds && window.AppMintAds.config().formats.app_open);Pause-o enquanto o usuário escolhe uma foto, já que o seletor de fotos do sistema o tira do app por um instante:
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);
});Observações: Vem ativado por padrão. Na inicialização a frio o anúncio aparece quando a página está pronta, ou quando termina de carregar em até 15 segundos; um carregamento posterior é guardado para a próxima vez. Um anúncio de abertura do app com mais de 4 horas nunca é exibido. Você também pode exibir um você mesmo com AppMintAds.show('app_open').
AppMintAds.banner helper#
window.AppMintAds.banner.attach(target, opts) / .detach(slotOrElement)
Exemplo
Coloca um banner ou um anúncio de retângulo médio de 300×250 em qualquer lugar da sua página, sobre um elemento que você escolher.
Retorna: attach retorna uma Promise de {ok:true, slot, width, height} (px CSS) quando o anúncio carregou, ou {ok:false, slot?, code, message?}. detach não retorna nada. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio de banner (ou retângulo médio).
O elemento apenas reserva o espaço. O anúncio é desenhado sobre ele e o acompanha quando a página rola ou é redimensionada:
<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>Retângulo médio (um por vez) e a remoção de um banner novamente:
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;
}Tente um espaço de novo mais tarde quando nenhum anúncio estava disponível, liberando antes o espaço que falhou para que ele não consuma um dos dois:
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);Observações: target é um elemento ou um seletor CSS; size é 'banner' (padrão, largura adaptativa, no máximo 90 dp de altura), 'medium_rectangle' ou 'mrec'. No máximo dois espaços ao mesmo tempo, e um espaço cujo anúncio falhou ao carregar ainda conta até você desanexá-lo. detach precisa do elemento ou do ID do espaço retornado por attach, não de um seletor. Remover o elemento do DOM também o desanexa. Códigos: no_element, duplicate_slot, too_many_slots, detached, no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode, internal.
AppMintAds.config helper#
window.AppMintAds.config()
Exemplo
Informa à página qual modo de controle de anúncios o build usa e quais formatos de anúncio ele pode servir.
Retorna: um objeto, de forma síncrona: {control: 'automatic' | 'developer', formats: {banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}} (cada formato true / false). Em caso de erro interno retorna {control:'automatic', formats:{}}. Requer: nada; window.AppMintAds existe apenas dentro do app instalado.
Mostre apenas a interface de anúncios que este build realmente pode 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);Escolha o formato com recompensa que o build tem para um botão de "bônus" e oculte o botão quando não houver nenhum dos dois:
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);
});Escolha o tamanho do anúncio para o final de um artigo: um retângulo médio quando o build tiver um, caso contrário um 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; }
});Observações: Um formato é true quando os anúncios estão ativados e o build tem um ID de unidade de anúncio para ele. No modo Automático (control: 'automatic') toda chamada de load / show / banner.attach responde code: 'automatic_mode'; use WebToApk.showRewardedAd() / showInterstitialAd() lá. Escolha Controle de anúncios → Controlado pelo desenvolvedor no assistente de build para conduzir os anúncios a partir da página.
AppMintAds.isReady helper#
window.AppMintAds.isReady(type)
Exemplo
Informa se um anúncio em tela cheia deste tipo está carregado e pode ser exibido agora.
Retorna: true / false, de forma síncrona. type: 'interstitial', 'rewarded', 'rewarded_interstitial' ou 'app_open'. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para o tipo.
Mantenha um botão "Assistir anúncio" sincronizado com o anúncio, usando o evento de status 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
}Ofereça "dobrar suas moedas" na tela de resultados apenas quando um anúncio com recompensa já estiver 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
};
}Em um botão de dica, reproduza qualquer formato com recompensa que esteja carregado agora:
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();
});Observações: banner e mrec sempre respondem false (eles são anexados, não exibidos). Um intersticial dentro do intervalo de 60 segundos responde false; um anúncio de abertura do app com mais de 4 horas responde false. No modo Automático isto é sempre false.
AppMintAds.load helper#
window.AppMintAds.load(type)
Exemplo
Carrega um anúncio em tela cheia para que um show(type) posterior possa exibi-lo imediatamente.
Retorna: uma Promise que resolve (nunca rejeita) com {ok:true}, {ok:true, cached:true} quando um já está esperando, ou {ok:false, code, reason, message?, adCode?}. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para o 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();A vida inteira de cada requisição também é transmitida no 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); };Carregue o intersticial quando uma fase começar, para que esteja lá quando ela terminar, e pare de pedir quando o usuário tiver removido os anúncios:
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();
}Observações: Códigos: no_fill, network, invalid_unit, internal (do AdMob), automatic_mode, unknown_type, ad_free, disabled, consent e use_slot para banner / mrec (use AppMintAds.banner.attach). O app já pré-carrega cada tipo de tela cheia que tenha um ID de unidade e carrega o próximo após cada exibição, então load serve principalmente para saber quando o botão pode ser habilitado.
AppMintAds.show helper#
window.AppMintAds.show(type)
Exemplo
Exibe um anúncio em tela cheia carregado: interstitial, rewarded, rewarded_interstitial ou app_open.
Retorna: uma Promise que resolve (nunca rejeita) quando o anúncio fechou: {ok:true, shown:true, earned, reward?} (reward = {type, amount} para os formatos com recompensa), ou {ok:false, shown:false, code, reason, message?} quando nada foi exibido. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para o tipo.
Com recompensa: chame dentro do toque do próprio usuário. Esse toque é a adesão do usuário, então o anúncio toca de imediato. Conceda apenas quando earned for verdadeiro:
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: em uma pausa natural. Continue seja qual for a resposta:
async function levelFinished() {
if (window.AppMintAds && window.AppMintAds.isReady('interstitial')) {
await window.AppMintAds.show('interstitial'); // resolves when the ad closes
}
showNextLevel();
}Intersticial com recompensa atrás da sua própria tela de introdução com um "Não, obrigado" claro. O toque em "Assistir" é a adesão:
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();
});Observações: Um show() com recompensa ou intersticial com recompensa que NÃO vem de um toque do usuário (um timer, carregamento de página, mudança de rota) primeiro mostra uma tela "Assistir a um anúncio? / Não, obrigado", e "Não, obrigado" responde declined. Nunca dispare formatos com recompensa dessa forma. Outros códigos: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (60 s entre intersticiais), not_loaded (um novo carregamento começa para a próxima vez), show_failed, internal. No modo Automático use WebToApk.showRewardedAd().
adsAppOpenEnable ponte#
window.WebToApk.adsAppOpenEnable(enabled: Boolean)
Permite que a página pause/retome o anúncio de abertura do app ao voltar de outro app (ativado por padrão quando há uma unidade definida).
Exemplo
A metade nativa de AppMintAds.appOpen.setEnabled(on): ela pausa ou retoma o anúncio de abertura do app. As páginas usam AppMintAds.appOpen.setEnabled.
Retorna: nada. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio de abertura do 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); }Escolher uma foto sai do app para o seletor do sistema, e a volta contaria como um retorno. Pause o anúncio de abertura do app durante esse processo:
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);Membros do seu próprio plano pago nunca o veem. A opção não é salva, então defina-a cedo a cada inicialização:
// 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);
}Observações: Assinatura bruta adsAppOpenEnable(enabled). Ativado por padrão quando uma unidade de abertura do app está definida. O anúncio de abertura do app aparece na inicialização a frio (se carregar em até 15 segundos) e quando o usuário volta de outro app; um anúncio com mais de 4 horas nunca é exibido.
adsConfig ponte#
window.WebToApk.adsConfig(): String
`{control, formats:{banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}}` - os formatos de anúncio que este build pode servir.
Exemplo
Lê quais formatos de anúncio este build pode servir e qual modo de controle de anúncios ele usa. As páginas normalmente chamam AppMintAds.config(), que interpreta isso para você.
Retorna: uma string JSON: {"control":"automatic"|"developer","formats":{"banner":bool,"mrec":bool,"interstitial":bool,"rewarded":bool,"rewarded_interstitial":bool,"app_open":bool}}. Um formato é true quando os anúncios estão ativados e o build tem um ID de unidade de anúncio para ele. Requer: nada.
Primeiro, a 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;
}
}A chamada bruta, se você não usa 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); }
}Reserve espaço apenas para os formatos que este build realmente tem e, caso contrário, devolva a caixa vazia para a página:
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
}Observações: No modo Automático formats ainda informa quais unidades existem, mas AppMintAds.load/show respondem code: 'automatic_mode'; use WebToApk.showRewardedAd() / showInterstitialAd() lá.
adsIsReady ponte#
window.WebToApk.adsIsReady(type: String): Boolean
Verdadeiro quando um [type] em tela cheia está carregado e pode ser exibido agora.
Exemplo
Informa se um anúncio em tela cheia de um tipo está carregado e pode ser exibido agora (anúncios controlados pelo desenvolvedor). As páginas normalmente chamam AppMintAds.isReady(type).
Retorna: true / false, de forma síncrona. type é 'interstitial', 'rewarded', 'rewarded_interstitial' ou 'app_open'; 'banner' / 'mrec' sempre respondem false. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para esse tipo.
Primeiro, a 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 readinessA chamada bruta:
var b = window.WebToApk;
var ready = !!(b && typeof b.adsIsReady === 'function' && b.adsIsReady('interstitial'));Mostre um intersticial no fim de uma fase apenas quando houver um esperando, e nunca segure o jogador quando não houver:
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
}
}Observações: Um intersticial dentro do seu intervalo de 60 segundos responde false. Um anúncio de abertura do app com mais de 4 horas responde false (o Google diz para não o exibir). No modo Automático isto é sempre false; use WebToApk.isRewardedAdReady() lá.
adsLoad ponte#
window.WebToApk.adsLoad(type: String, callbackId: String)
Carrega um [type] em tela cheia; a promise de [callbackId] resolve com o resultado.
Exemplo
A metade nativa de AppMintAds.load(type): ela começa a carregar um anúncio em tela cheia e responde ao callback do shim. As páginas usam AppMintAds.load(type), que retorna uma Promise; não chame isto diretamente.
Retorna: (por meio de AppMintAds.load) uma Promise de {ok:true}, {ok:true, cached:true} quando um já está carregado, ou {ok:false, code, reason, message?, adCode?}. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para o 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
}Tente de novo um carregamento que falhou por um motivo passageiro e pare nos motivos que uma nova tentativa não resolve:
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;
});
}Aqueça dois formatos antes de uma sessão de jogo começar e oculte todos os botões de anúncio quando o usuário não tiver anúncios:
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);
});
});
}Observações: Códigos de falha: no_fill, network, invalid_unit, internal (do AdMob, com adCode), e automatic_mode, unknown_type, ad_free, disabled, consent e use_slot para banner / mrec (esses carregam com AppMintAds.banner.attach). O app já pré-carrega todo tipo de tela cheia que tenha um ID de unidade e recarrega após cada exibição. A assinatura bruta é adsLoad(type, callbackId); a resposta vai para um resolvedor interno, então uma chamada direta não lhe dá resultado.
adsShow ponte#
window.WebToApk.adsShow(type: String, callbackId: String, userGesture: Boolean)
Exibe um [type] em tela cheia carregado; resolve quando ele fecha, com earned/reward para formatos com recompensa. [userGesture] é a leitura de `navigator.userActivation` do shim - um formato com recompensa pedido sem ela recebe antes a tela de introdução do AdMob (veja DeveloperAds.offerRewarded).
Exemplo
A metade nativa de AppMintAds.show(type): ela exibe um anúncio em tela cheia carregado e responde quando ele fecha. As páginas usam AppMintAds.show(type); não chame isto diretamente.
Retorna: (por meio de AppMintAds.show) uma Promise de {ok:true, shown:true, earned, reward?} depois que o anúncio fecha (reward = {type, amount} para formatos com recompensa), ou {ok:false, shown:false, code, reason, message?}. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio para o tipo.
Chame-o a partir do toque do usuário. O shim passa navigator.userActivation.isActive como terceiro 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.');
});
});Ofereça um intersticial com recompensa a partir do toque do próprio usuário em "Dobrar meu bônus" e siga em frente seja qual for a resposta:
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
});
});Pause a música do seu jogo enquanto qualquer anúncio em tela cheia estiver na tela, usando o fluxo 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
}Observações: Assinatura bruta adsShow(type, callbackId, userGesture). Quando userGesture é false para rewarded ou rewarded_interstitial, o app primeiro pergunta "Assistir a um anúncio? / Não, obrigado" e um "não" responde declined. Outros códigos: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (intersticial, 60 s), not_loaded (um carregamento começa para a próxima vez), show_failed, internal.
adsSlotAttach ponte#
window.WebToApk.adsSlotAttach(slotId: String, type: String, widthCss: Int, callbackId: String)
Cria uma sobreposição de banner/mrec para o espaço [slotId] da página, dimensionada em [widthCss]; resolve com o tamanho do anúncio depois de carregado.
Exemplo
A metade nativa de AppMintAds.banner.attach(element, {size}): ela cria um anúncio de banner ou retângulo médio sobre um elemento da página. As páginas usam AppMintAds.banner.attach; não chame isto diretamente.
Retorna: (por meio de AppMintAds.banner.attach) uma Promise de {ok:true, slot, width, height} em pixels CSS quando o anúncio carregou, ou {ok:false, slot, code, message?}. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um ID de unidade de anúncio de banner (ou retângulo médio).
<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>Um retângulo médio em uma tela de resultados que devolve o espaço quando nenhum anúncio chega:
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;
}Conte impressões e cliques por espaço a partir do fluxo appmint:ad, usando como chave o ID do espaço que attach retornou:
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);
}
});Observações: Assinatura bruta adsSlotAttach(slotId, type, widthCss, callbackId); type é 'banner' ou 'mrec'. Um WebView não pode conter uma view nativa dentro da página, então o anúncio é desenhado sobre o seu elemento e o acompanha na rolagem e no redimensionamento (veja adsSlotRect). No máximo dois espaços ao mesmo tempo, e apenas um retângulo médio. Códigos extras: duplicate_slot, too_many_slots, detached, internal, mais os códigos de carregamento (no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode).
adsSlotDetach ponte#
window.WebToApk.adsSlotDetach(slotId: String)
Remove uma sobreposição de banner.
Exemplo
A metade nativa de AppMintAds.banner.detach(slotOrElement): ela remove uma sobreposição de banner e libera seu espaço. As páginas usam AppMintAds.banner.detach.
Retorna: nada. Requer: Controle de anúncios → Controlado pelo desenvolvedor.
Remova o banner antes de uma tela onde um anúncio não cabe (um player de vídeo, um checkout):
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' });
}Em um app de página única, libere o banner quando a rota que o contém desaparecer. attach marca o elemento imediatamente, então desanexar pelo elemento funciona mesmo enquanto o anúncio ainda está carregando:
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' });
}
}Depois de uma compra de "Remover anúncios" as sobreposições somem; desanexe também os espaços e elimine o espaço que eles reservavam:
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();
});
});Observações: Assinatura bruta adsSlotDetach(slotId). detach recebe o elemento ou a string slot que attach retornou, não um seletor CSS. Uma Promise de attach ainda esperando seu anúncio responde {ok:false, code:'detached'}. Remover o elemento do DOM também desanexa o espaço.
adsSlotRect ponte#
window.WebToApk.adsSlotRect(slotId: String, x: Float, y: Float, w: Float, h: Float, visible: Boolean)
Interno: o retângulo atual do elemento do espaço na viewport (px CSS) - move a sobreposição.
Exemplo
Interno: move uma sobreposição de banner para a posição atual do seu elemento. O helper AppMintAds.banner chama isso por você na rolagem, no redimensionamento, na mudança de visibilidade e a cada 500 ms; as páginas usam AppMintAds.banner.attach e nunca chamam isto.
Retorna: nada. Requer: Controle de anúncios → Controlado pelo desenvolvedor e um espaço criado por AppMintAds.banner.attach.
Tudo o que uma página faz é dar ao elemento do espaço um lugar no layout. O helper o mede e mantém o anúncio sobre ele:
<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>Um banner dentro de uma lista com rolagem, não da página: o helper escuta o scroll na fase de captura, então o anúncio acompanha também o contêiner 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>O anúncio é desenhado acima da página, então seu próprio diálogo ficaria por baixo dele. Oculte o espaço do anúncio enquanto o diálogo estiver aberto:
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
});Observações: Assinatura bruta adsSlotRect(slotId, x, y, w, h, visible), em pixels CSS relativos à viewport. A sobreposição fica oculta enquanto o elemento está fora da tela, tem tamanho zero, tem visibility: hidden, ou a página está em segundo plano. Se o elemento sair do DOM, o helper desanexa o espaço sozinho.
isInterstitialAdReady ponte#
window.WebToApk.isInterstitialAdReady(): Boolean
Verdadeiro quando um intersticial está carregado E já passou do intervalo (corresponde exatamente ao que showInterstitialAd() vai aceitar - sem um "pronto" que depois se recusa a exibir).
Exemplo
Informa se um anúncio intersticial está carregado E já passou do intervalo de 60 segundos, de modo que showInterstitialAd() o exibiria agora (controle automático de anúncios).
Retorna: true / false, de forma síncrona. Requer: Anúncios ativados e Anúncio em tela cheia → Anúncio 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();
});Mostre um anúncio após cada terceiro quiz concluído, e apenas quando um realmente seria exibido:
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
}Mostre um breve aviso "Anúncio em 3…" apenas quando um anúncio estiver pronto e depois continue quando ele fechar ou não puder ser exibido:
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);Observações: true aqui significa que a exibição não será recusada por not_loaded nem cooldown. O consentimento ou uma compra de "Remover anúncios" ainda podem impedi-la (appmint:ad-unavailable). Em builds Controlados pelo desenvolvedor use AppMintAds.isReady('interstitial').
isRewardedAdReady ponte#
window.WebToApk.isRewardedAdReady(): Boolean
Verdadeiro quando um anúncio com recompensa está carregado e pode ser exibido agora. Deixe a página habilitar/desabilitar seu botão "Assistir anúncio" com base nisto (consulte ou chame antes de exibir).
Exemplo
Informa se um anúncio com recompensa está carregado e pode ser exibido agora (controle automático de anúncios).
Retorna: true / false, de forma síncrona. Requer: Anúncios ativados e Anúncio em tela cheia → Anúncio com recompensa.
Habilite o botão "Assistir a um anúncio" apenas quando houver um anúncio 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);No fim de jogo, ofereça "Continuar" apenas quando um anúncio estiver esperando, para que o botão nunca leve a lugar nenhum:
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(); });Verifique no momento do toque e diga ao usuário o que está acontecendo, em vez de disparar uma exibição que será recusada:
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(); });Observações: Retorna false em um navegador, quando os anúncios foram removidos por compra e antes de o primeiro anúncio carregar. Depois que um anúncio é exibido, o próximo carrega sozinho. Em builds Controlados pelo desenvolvedor use AppMintAds.isReady('rewarded').
notifyUserAction ponte#
window.WebToApk.notifyUserAction()
Interno (chamado pelo contador de toques injetado, não pelo código do criador): ocorreu uma interação significativa do usuário na página. Conta para a cadência "intersticial a cada N ações" APENAS enquanto nenhuma navegação baseada em URL tiver sido observada - apps de HTML único que trocam de tela via mostrar/ocultar DOM nunca mudam a URL, então sem isto o contador de navegação deles ficava em 0 para sempre e os anúncios configurados nunca apareciam (classe de reclamação TicTrek, 26/07/2026). Apps de URL/SPA não são afetados: a primeira navegação real deles volta a contagem permanentemente para os sinais de URL.
Exemplo
Conta uma ação significativa do usuário para a configuração "anúncio em tela cheia a cada N ações". O app chama isso sozinho a partir do próprio contador de toques; as páginas normalmente nunca o chamam.
Retorna: nada. Requer: Anúncios ativados, um Anúncio em tela cheia com Modo de disparo → Após navegações e controle automático de anúncios.
Como funciona: uma página que troca de tela ocultando e mostrando elementos do DOM nunca muda de URL, então o app não consegue ver uma "navegação". Por isso o app injeta um contador de toques (no máximo uma contagem a cada 2 segundos) que chama este método. Assim que o app vê uma mudança real de URL (um carregamento de página, pushState ou um #hash), ele passa a contar mudanças de URL e este método para de contar.
Somente se as mudanças de tela não vierem de um toque (por exemplo um movimento de teclado ou controle), reporte-as você mesmo:
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');
});Um app em estilo de cartões em que o usuário desliza para a próxima tela (deslizar não é um toque em um botão, então o contador de toques nunca percebe):
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();
});Um jogo HTML5 jogado com um gamepad, contando uma ação por fase que o jogador inicia com o botão 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 });Observações: Não faz nada quando os anúncios estão desativados, removidos por compra, ou quando o build usa Controle de anúncios → Controlado pelo desenvolvedor (nesse caso você mesmo chama AppMintAds.show()). Um anúncio com recompensa acionado assim é sempre oferecido com a pergunta "Assistir a um anúncio? / Não, obrigado", nunca forçado. Não o chame a cada toque; isso mostraria anúncios com mais frequência do que você configurou.
showInterstitialAd ponte#
window.WebToApk.showInterstitialAd()
Exibe um intersticial AGORA (por exemplo entre fases / em uma ação deliberada). Não pode ser exibido agora → ignorado + 'appmint:ad-unavailable' com o motivo.
Exemplo
Exibe um anúncio intersticial em tela cheia agora, por exemplo entre duas fases de um jogo (controle automático de anúncios).
Retorna: nada. Quando o anúncio fecha, appmint:ad-closed é disparado com {adType:'interstitial', earned:false}. Quando ele não pode ser exibido, appmint:ad-unavailable é disparado com {adType:'interstitial', reason}. Requer: Anúncios ativados e Anúncio em tela cheia → Anúncio intersticial. Escolha Modo de disparo → Sob demanda se apenas a sua página deve decidir quando.
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(); }
});Pause o jogo e o som antes do anúncio e retome em qualquer uma das respostas:
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);Abra um artigo depois de um anúncio, como uma única Promise que resolve true quando o anúncio foi exibido e false quando foi pulado:
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); });Observações: O app mantém pelo menos 60 segundos entre dois intersticiais; uma chamada dentro desse intervalo responde cooldown. isInterstitialAdReady() já inclui o intervalo, então verificá-lo antes evita o evento. Exiba intersticiais em pausas naturais, nunca a cada toque. Em builds Controlados pelo desenvolvedor use AppMintAds.show('interstitial').
showRewardedAd ponte#
window.WebToApk.showRewardedAd()
Exibe um anúncio com recompensa AGORA (iniciado pelo usuário - a única forma em conformidade com a política de exibir anúncios com recompensa). Combine com o modo de disparo "Sob demanda" no assistente para ter anúncios com recompensa SEM nenhum anúncio automático de abertura do app/navegação. A recompensa é entregue de volta à página via deliverRewardToPage(): window.addEventListener('appmint:reward', e => { /* e.detail = {type,amount} */ }) Quando o anúncio fecha, 'appmint:ad-closed' é disparado com {adType:'rewarded', earned} - earned é false se o usuário saiu antes, então não conceda nada nesse caso. Não pode ser exibido agora → ignorado + 'appmint:ad-unavailable' disparado com o motivo.
Exemplo
Exibe um anúncio com recompensa agora, a partir do toque do próprio usuário em "Assistir a um anúncio" (controle automático de anúncios).
Retorna: nada. O resultado chega depois como eventos: appmint:reward {type, amount} quando o anúncio foi assistido até o fim, appmint:ad-closed {adType:'rewarded', earned} quando ele fecha, ou appmint:ad-unavailable {adType:'rewarded', reason} quando nada pôde ser exibido. Requer: Anúncios ativados, Anúncio em tela cheia → Anúncio com recompensa e Modo de disparo → Sob demanda para que nada apareça sozinho.
Conceda a recompensa apenas no handler de recompensa, nunca no toque:
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();
});Os mesmos três momentos também chamam hooks da página, se você preferir uma função para cada um (cada uma recebe o mesmo 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); };Envolva a chamada e seus eventos em uma única Promise, para que cada botão "Assistir a um anúncio" se leia como uma chamada assí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.');
});
});Observações: Chame apenas a partir de um handler de clique. Nunca o chame a partir de um timer, carregamento de página ou mudança de rota, e nunca coloque um recurso principal atrás dele. not_loaded também inicia um novo carregamento, então o próximo toque costuma funcionar. Em builds Controle de anúncios → Controlado pelo desenvolvedor use AppMintAds.show('rewarded') no lugar, que responde com uma Promise.