Создать APK бесплатно

JavaScript bridge API

Реклама

Показ interstitial или вознаграждаемой рекламы по вашему сигналу и уведомление оболочки о начале и завершении навигации, чтобы она не прерывала показ.

AppMintAds.appOpen помощник#

window.AppMintAds.appOpen.setEnabled(on)

Пример

Приостанавливает или возобновляет рекламу при открытии приложения — полноэкранную рекламу, показываемую при запуске и когда пользователь возвращается из другого приложения.

Возвращает: ничего. Требуется: Управление рекламой → Под контролем разработчика и ID рекламного блока при открытии приложения.

Приостановите её, пока пользователь намеренно ненадолго выходит из приложения (платёжное приложение, камера, меню «Поделиться»), чтобы при возвращении не показывалась реклама:

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);
  }
}

Проверьте, есть ли в сборке блок рекламы при открытии приложения вообще:

var hasAppOpen = !!(window.AppMintAds && window.AppMintAds.config().formats.app_open);

Приостановите её, пока пользователь выбирает фото, поскольку системный выбор фото ненадолго уводит его из приложения:

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);
});

Примечания: По умолчанию включено. При холодном запуске реклама показывается, когда страница готова или когда загрузка завершилась в течение 15 секунд; более поздняя загрузка сохраняется на следующий раз. Реклама при открытии приложения старше 4 часов никогда не показывается. Вы также можете показать её сами с помощью AppMintAds.show('app_open').

AppMintAds.banner помощник#

window.AppMintAds.banner.attach(target, opts) / .detach(slotOrElement)

Пример

Размещает баннер или рекламу «средний прямоугольник» 300×250 в любом месте вашей страницы, поверх выбранного вами элемента.

Возвращает: attach возвращает Promise с {ok:true, slot, width, height} (CSS px) после загрузки рекламы или {ok:false, slot?, code, message?}. detach ничего не возвращает. Требуется: Управление рекламой → Под контролем разработчика и ID баннерного (или среднего прямоугольника) блока.

Элемент лишь резервирует место. Реклама рисуется поверх него и следует за ним при прокрутке или изменении размера страницы:

<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>

Средний прямоугольник (по одному за раз) и повторное удаление баннера:

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;
}

Попробуйте блок снова позже, если реклама была недоступна, предварительно освободив неудавшийся блок, чтобы он не занимал одно из двух мест:

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);

Примечания: target — это элемент или CSS-селектор; size равен 'banner' (по умолчанию, адаптивная ширина, не выше 90 dp), 'medium_rectangle' или 'mrec'. Не более двух блоков одновременно, а блок, реклама которого не загрузилась, всё равно считается, пока вы его не отсоедините. detach нужен элемент или ID блока из attach, а не селектор. Удаление элемента из DOM тоже отсоединяет его. Коды: no_element, duplicate_slot, too_many_slots, detached, no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode, internal.

AppMintAds.config помощник#

window.AppMintAds.config()

Пример

Сообщает странице, какой режим управления рекламой использует сборка и какие форматы рекламы она может показывать.

Возвращает: объект, синхронно: {control: 'automatic' | 'developer', formats: {banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}} (каждый формат true / false). При внутренней ошибке возвращает {control:'automatic', formats:{}}. Требуется: ничего; window.AppMintAds существует только внутри установленного приложения.

Показывайте только тот рекламный интерфейс, который эта сборка действительно может обслужить:

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);

Выберите вознаграждаемый формат, который есть в сборке, для кнопки «бонус» и скройте кнопку, если нет ни одного:

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);
});

Выберите размер рекламы для конца статьи: средний прямоугольник, если он есть в сборке, иначе баннер:

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; }
});

Примечания: Формат равен true, когда реклама включена и в сборке есть ID блока для него. В автоматическом режиме (control: 'automatic') каждый вызов load / show / banner.attach возвращает code: 'automatic_mode'; используйте там WebToApk.showRewardedAd() / showInterstitialAd(). Выберите Управление рекламой → Под контролем разработчика в мастере сборки, чтобы управлять рекламой со страницы.

AppMintAds.isReady помощник#

window.AppMintAds.isReady(type)

Пример

Сообщает, загружена ли полноэкранная реклама этого типа и можно ли показать её прямо сейчас.

Возвращает: true / false, синхронно. type: 'interstitial', 'rewarded', 'rewarded_interstitial' или 'app_open'. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа.

Синхронизируйте кнопку «Смотреть рекламу» с рекламой через событие статуса 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
}

Предлагайте «удвоить монеты» на экране результатов, только если вознаграждаемая реклама уже ждёт:

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
  };
}

В кнопке подсказки проигрывайте тот вознаграждаемый формат, который сейчас загружен:

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();
});

Примечания: banner и mrec всегда возвращают false (они подключаются, а не показываются). Interstitial в 60-секундной паузе возвращает false; реклама при открытии приложения старше 4 часов возвращает false. В автоматическом режиме это всегда false.

AppMintAds.load помощник#

window.AppMintAds.load(type)

Пример

Загружает полноэкранную рекламу, чтобы последующий show(type) мог показать её сразу.

Возвращает: Promise, который выполняется (никогда не отклоняется) с {ok:true}, {ok:true, cached:true}, если реклама уже ждёт, или {ok:false, code, reason, message?, adCode?}. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа (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();

Вся жизнь каждого запроса также передаётся событием 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); };

Загружайте interstitial при начале уровня, чтобы он был готов к его концу, и перестаньте запрашивать, когда пользователь убрал рекламу:

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();
}

Примечания: Коды: no_fill, network, invalid_unit, internal (от AdMob), automatic_mode, unknown_type, ad_free, disabled, consent и use_slot для banner / mrec (используйте AppMintAds.banner.attach). Приложение уже заранее загружает каждый полноэкранный тип с ID блока и загружает следующий после каждого показа, поэтому load в основном нужен, чтобы знать, когда можно включить кнопку.

AppMintAds.show помощник#

window.AppMintAds.show(type)

Пример

Показывает загруженную полноэкранную рекламу: interstitial, rewarded, rewarded_interstitial или app_open.

Возвращает: Promise, который выполняется (никогда не отклоняется), когда реклама закрылась: {ok:true, shown:true, earned, reward?} (reward = {type, amount} для вознаграждаемых форматов) или {ok:false, shown:false, code, reason, message?}, если ничего не было показано. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа.

Вознаграждаемая: вызывайте внутри нажатия самого пользователя. Это нажатие — согласие пользователя, поэтому реклама играет сразу. Начисляйте награду, только когда earned равно true:

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.');
});

Interstitial: в естественной паузе. Продолжайте при любом ответе:

async function levelFinished() {
  if (window.AppMintAds && window.AppMintAds.isReady('interstitial')) {
    await window.AppMintAds.show('interstitial');    // resolves when the ad closes
  }
  showNextLevel();
}

Вознаграждаемый interstitial за вашим собственным вводным экраном с понятной кнопкой «Нет, спасибо». Нажатие «Смотреть» — это согласие:

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();
});

Примечания: Вознаграждаемый или вознаграждаемый interstitial show(), который НЕ вызван нажатием пользователя (таймер, загрузка страницы, смена маршрута), сначала показывает экран «Посмотреть рекламу? / Нет, спасибо», а «Нет, спасибо» даёт declined. Никогда не запускайте вознаграждаемые форматы так. Другие коды: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (60 с между interstitial), not_loaded (новая загрузка начинается на следующий раз), show_failed, internal. В автоматическом режиме используйте WebToApk.showRewardedAd().

adsAppOpenEnable мост#

window.WebToApk.adsAppOpenEnable(enabled: Boolean)

Позволяет странице приостанавливать/возобновлять рекламу при открытии приложения при возврате из другого приложения (по умолчанию включено, если задан блок).

Пример

Нативная часть AppMintAds.appOpen.setEnabled(on): она приостанавливает или возобновляет рекламу при открытии приложения. Страницы используют AppMintAds.appOpen.setEnabled.

Возвращает: ничего. Требуется: Управление рекламой → Под контролем разработчика и ID рекламного блока при открытии приложения.

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); }

Выбор фото уводит из приложения в системный выбор, и возвращение считалось бы возвратом. Приостановите рекламу при открытии приложения на это время:

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);

Участники вашего собственного платного тарифа никогда её не видят. Переключатель не сохраняется, поэтому задавайте его рано при каждом запуске:

// 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);
}

Примечания: Исходная сигнатура adsAppOpenEnable(enabled). По умолчанию включено, когда задан блок рекламы при открытии приложения. Эта реклама показывается при холодном запуске (если загрузилась за 15 секунд) и когда пользователь возвращается из другого приложения; реклама старше 4 часов никогда не показывается.

adsConfig мост#

window.WebToApk.adsConfig(): String

`{control, formats:{banner, mrec, interstitial, rewarded, rewarded_interstitial, app_open}}` — форматы рекламы, которые может показывать эта сборка.

Пример

Считывает, какие форматы рекламы может показывать эта сборка и какой режим управления рекламой она использует. Страницы обычно вызывают AppMintAds.config(), который разбирает это за вас.

Возвращает: строку JSON: {"control":"automatic"|"developer","formats":{"banner":bool,"mrec":bool,"interstitial":bool,"rewarded":bool,"rewarded_interstitial":bool,"app_open":bool}}. Формат равен true, когда реклама включена и в сборке есть ID блока для него. Требуется: ничего.

Сначала публичный API:

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;
  }
}

Прямой вызов, если вы не используете 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); }
}

Резервируйте место только для форматов, которые действительно есть в этой сборке, а пустой блок возвращайте странице:

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
}

Примечания: В автоматическом режиме formats по-прежнему сообщает, какие блоки есть, но AppMintAds.load/show возвращают code: 'automatic_mode'; используйте там WebToApk.showRewardedAd() / showInterstitialAd().

adsIsReady мост#

window.WebToApk.adsIsReady(type: String): Boolean

True, когда полноэкранная реклама [type] загружена и может быть показана прямо сейчас.

Пример

Сообщает, загружена ли полноэкранная реклама одного типа и можно ли показать её сейчас (реклама под контролем разработчика). Страницы обычно вызывают AppMintAds.isReady(type).

Возвращает: true / false, синхронно. type равен 'interstitial', 'rewarded', 'rewarded_interstitial' или 'app_open'; 'banner' / 'mrec' всегда возвращают false. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа.

Сначала публичный API:

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 readiness

Прямой вызов:

var b = window.WebToApk;
var ready = !!(b && typeof b.adsIsReady === 'function' && b.adsIsReady('interstitial'));

Показывайте interstitial в конце уровня, только когда он ждёт, и никогда не задерживайте игрока, когда его нет:

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
  }
}

Примечания: Interstitial в пределах 60-секундной паузы возвращает false. Реклама при открытии приложения старше 4 часов возвращает false (Google просит не показывать её). В автоматическом режиме это всегда false; используйте там WebToApk.isRewardedAdReady().

adsLoad мост#

window.WebToApk.adsLoad(type: String, callbackId: String)

Загружает полноэкранную рекламу [type]; promise для [callbackId] выполняется с результатом.

Пример

Нативная часть AppMintAds.load(type): она начинает загрузку полноэкранной рекламы и отвечает на колбэк оболочки. Страницы используют AppMintAds.load(type), который возвращает Promise; не вызывайте это напрямую.

Возвращает: (через AppMintAds.load) Promise с {ok:true}, {ok:true, cached:true}, если реклама уже загружена, или {ok:false, code, reason, message?, adCode?}. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа.

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
}

Повторите загрузку, которая не удалась по временной причине, и остановитесь при причинах, которые повтор не исправит:

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;
  });
}

Прогрейте два формата перед началом игровой сессии и скройте все рекламные кнопки, если у пользователя вообще нет рекламы:

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);
    });
  });
}

Примечания: Коды ошибок: no_fill, network, invalid_unit, internal (от AdMob, с adCode) и automatic_mode, unknown_type, ad_free, disabled, consent, и use_slot для banner / mrec (они загружаются через AppMintAds.banner.attach). Приложение уже заранее загружает каждый полноэкранный тип с ID блока и перезагружает после каждого показа. Исходная сигнатура: adsLoad(type, callbackId); её ответ идёт во внутренний обработчик, поэтому прямой вызов не даст вам результата.

adsShow мост#

window.WebToApk.adsShow(type: String, callbackId: String, userGesture: Boolean)

Показывает загруженную полноэкранную рекламу [type]; завершается при закрытии, с earned/reward для вознаграждаемых форматов. [userGesture] — показание `navigator.userActivation` оболочки: вознаграждаемый формат, запрошенный без жеста, сначала получает вводный экран AdMob (см. DeveloperAds.offerRewarded).

Пример

Нативная часть AppMintAds.show(type): она показывает загруженную полноэкранную рекламу и отвечает, когда та закрывается. Страницы используют AppMintAds.show(type); не вызывайте это напрямую.

Возвращает: (через AppMintAds.show) Promise с {ok:true, shown:true, earned, reward?} после закрытия рекламы (reward = {type, amount} для вознаграждаемых форматов) или {ok:false, shown:false, code, reason, message?}. Требуется: Управление рекламой → Под контролем разработчика и ID блока для этого типа.

Вызывайте из нажатия пользователя. Оболочка передаёт navigator.userActivation.isActive третьим аргументом (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.');
  });
});

Предложите вознаграждаемый interstitial по собственному нажатию пользователя «Удвоить мой бонус» и идите дальше при любом ответе:

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
  });
});

Ставьте музыку игры на паузу, пока на экране любая полноэкранная реклама, используя поток 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
}

Примечания: Исходная сигнатура adsShow(type, callbackId, userGesture). Когда userGesture равно false для rewarded или rewarded_interstitial, приложение сначала спрашивает «Посмотреть рекламу? / Нет, спасибо», и ответ «нет» даёт declined. Другие коды: automatic_mode, unknown_type, ad_free, disabled, consent, not_full_screen, showing, cooldown (interstitial, 60 с), not_loaded (загрузка начинается на следующий раз), show_failed, internal.

adsSlotAttach мост#

window.WebToApk.adsSlotAttach(slotId: String, type: String, widthCss: Int, callbackId: String)

Создаёт баннерную/mrec-накладку для блока страницы [slotId] шириной [widthCss]; после загрузки возвращает размер рекламы.

Пример

Нативная часть AppMintAds.banner.attach(element, {size}): она создаёт баннерную рекламу или рекламу «средний прямоугольник» поверх элемента страницы. Страницы используют AppMintAds.banner.attach; не вызывайте это напрямую.

Возвращает: (через AppMintAds.banner.attach) Promise с {ok:true, slot, width, height} в CSS-пикселях после загрузки рекламы или {ok:false, slot, code, message?}. Требуется: Управление рекламой → Под контролем разработчика и ID баннерного (или среднего прямоугольника) блока.

<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>

Средний прямоугольник на экране результатов, который освобождает место, если реклама не пришла:

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;
}

Считайте показы и клики по каждому блоку из потока appmint:ad по ID блока, который вернул attach:

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);
  }
});

Примечания: Исходная сигнатура adsSlotAttach(slotId, type, widthCss, callbackId); type равен 'banner' или 'mrec'. WebView не может держать нативный вид внутри страницы, поэтому реклама рисуется поверх вашего элемента и следует за ним при прокрутке и изменении размера (см. adsSlotRect). Не более двух блоков одновременно и только один средний прямоугольник. Дополнительные коды: duplicate_slot, too_many_slots, detached, internal, плюс коды загрузки (no_fill, network, invalid_unit, ad_free, disabled, consent, automatic_mode).

adsSlotDetach мост#

window.WebToApk.adsSlotDetach(slotId: String)

Удаляет баннерную накладку.

Пример

Нативная часть AppMintAds.banner.detach(slotOrElement): она удаляет баннерную накладку и освобождает её блок. Страницы используют AppMintAds.banner.detach.

Возвращает: ничего. Требуется: Управление рекламой → Под контролем разработчика.

Уберите баннер перед экраном, где реклама неуместна (видеоплеер, оформление заказа):

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' });
}

В одностраничном приложении освобождайте баннер, когда маршрут, который его содержит, исчезает. attach сразу помечает элемент, поэтому отсоединение по элементу работает, даже пока реклама ещё загружается:

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' });
  }
}

После покупки «Убрать рекламу» накладки исчезают; отсоедините и блоки и верните страницу место, которое они занимали:

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();
  });
});

Примечания: Исходная сигнатура adsSlotDetach(slotId). detach принимает элемент или строку slot, которую вернул attach, а не CSS-селектор. Promise от attach, всё ещё ждущий рекламу, получает {ok:false, code:'detached'}. Удаление элемента из DOM тоже отсоединяет блок.

adsSlotRect мост#

window.WebToApk.adsSlotRect(slotId: String, x: Float, y: Float, w: Float, h: Float, visible: Boolean)

Внутренний метод: текущий прямоугольник элемента блока в области просмотра (CSS px) — перемещает накладку.

Пример

Внутренний метод: перемещает баннерную накладку в текущую позицию её элемента. Помощник AppMintAds.banner вызывает его за вас при прокрутке, изменении размера, смене видимости и каждые 500 мс; страницы используют AppMintAds.banner.attach и никогда не вызывают это.

Возвращает: ничего. Требуется: Управление рекламой → Под контролем разработчика и блок, созданный AppMintAds.banner.attach.

Странице нужно лишь дать элементу блока место в вёрстке. Помощник измеряет его и держит рекламу поверх него:

<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>

Баннер внутри прокручиваемого списка, а не страницы: помощник слушает прокрутку на фазе захвата, поэтому реклама следует и за внутренним контейнером:

<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>

Реклама рисуется поверх страницы, поэтому ваш диалог оказался бы под ней. Скройте блок, пока диалог открыт:

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
});

Примечания: Исходная сигнатура adsSlotRect(slotId, x, y, w, h, visible), в CSS-пикселях относительно области просмотра. Накладка скрыта, пока элемент вне экрана, имеет нулевой размер, имеет visibility: hidden или страница в фоне. Если элемент покидает DOM, помощник сам отсоединяет блок.

isInterstitialAdReady мост#

window.WebToApk.isInterstitialAdReady(): Boolean

True, когда interstitial загружен И прошла его пауза (в точности совпадает с тем, что примет showInterstitialAd() — никакого «готово», которое потом отказывается показываться).

Пример

Сообщает, загружен ли interstitial И прошла ли его 60-секундная пауза, то есть showInterstitialAd() показал бы его сейчас (автоматическое управление рекламой).

Возвращает: true / false, синхронно. Требуется: реклама включена и Полноэкранная реклама → Interstitial.

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();
});

Показывайте рекламу после каждой третьей завершённой викторины и только когда она действительно покажется:

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
}

Показывайте короткое уведомление «Реклама через 3…» только когда реклама готова, затем продолжайте, когда она закроется или не сможет показаться:

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);

Примечания: true здесь означает, что показ не будет отклонён из-за not_loaded или cooldown. Согласие или покупка «Убрать рекламу» всё равно могут его остановить (appmint:ad-unavailable). В сборках Под контролем разработчика используйте AppMintAds.isReady('interstitial').

isRewardedAdReady мост#

window.WebToApk.isRewardedAdReady(): Boolean

True, когда вознаграждаемая реклама загружена и может быть показана прямо сейчас. Пусть страница включает/отключает свою кнопку «Смотреть рекламу» по этому значению (опрашивайте или вызывайте перед показом).

Пример

Сообщает, загружена ли вознаграждаемая реклама и можно ли показать её прямо сейчас (автоматическое управление рекламой).

Возвращает: true / false, синхронно. Требуется: реклама включена и Полноэкранная реклама → Вознаграждаемая реклама.

Включайте кнопку «Смотреть рекламу» только когда реклама ждёт:

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);

При завершении игры предлагайте «Продолжить» только когда реклама ждёт, чтобы кнопка никогда не вела в никуда:

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(); });

Проверьте в момент нажатия и скажите пользователю, что происходит, вместо того чтобы запускать показ, который будет отклонён:

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(); });

Примечания: Возвращает false в браузере, когда реклама убрана покупкой и до загрузки первой рекламы. После показа следующая загружается сама. В сборках Под контролем разработчика используйте AppMintAds.isReady('rewarded').

notifyUserAction мост#

window.WebToApk.notifyUserAction()

Внутренний метод (вызывается внедрённым счётчиком нажатий, а не кодом автора): на странице произошло одно значимое действие пользователя. Засчитывается в частоту «interstitial каждые N действий» ТОЛЬКО пока не было замечено ни одной навигации по URL — одностраничные HTML-приложения, переключающие экраны показом/скрытием DOM, никогда не меняют URL, поэтому без этого их счётчик навигации вечно оставался на 0 и настроенная реклама не показывалась (жалоба класса TicTrek, 26.07.2026). URL/SPA-приложения не затронуты: их первая настоящая навигация навсегда возвращает подсчёт к сигналам URL.

Пример

Засчитывает одно значимое действие пользователя в настройку «полноэкранная реклама каждые N действий». Приложение вызывает это само из своего счётчика нажатий; страницы обычно никогда его не вызывают.

Возвращает: ничего. Требуется: реклама включена, Полноэкранная реклама с Режим запуска → После переходов и автоматическое управление рекламой.

Как это работает: страница, переключающая экраны скрытием и показом элементов DOM, никогда не меняет свой URL, поэтому приложение не видит «навигацию». Поэтому приложение внедряет счётчик нажатий (не чаще одного раза в 2 секунды), который вызывает этот метод. Как только приложение видит одно настоящее изменение URL (загрузка страницы, pushState или #hash), оно считает изменения URL, а этот метод перестаёт считать.

Только если смены экрана происходят не от нажатия (например, от клавиатуры или геймпада), сообщайте о них сами:

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');
});

Приложение с карточками, где пользователь листает к следующему экрану (свайп — это не нажатие кнопки, поэтому счётчик нажатий его не видит):

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();
});

HTML5-игра с геймпадом, засчитывающая одно действие за каждый уровень, который игрок начинает кнопкой 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 });

Примечания: Ничего не делает, если реклама выключена, убрана покупкой или сборка использует Управление рекламой → Под контролем разработчика (там вы вызываете AppMintAds.show() сами). Вознаграждаемая реклама, показанная таким способом, всегда предлагается вопросом «Посмотреть рекламу? / Нет, спасибо» и никогда не навязывается. Не вызывайте её при каждом нажатии: реклама будет показываться чаще, чем вы настроили.

showInterstitialAd мост#

window.WebToApk.showInterstitialAd()

Показать interstitial СЕЙЧАС (например, между уровнями / при осознанном действии). Если сейчас показать нельзя — пропуск + 'appmint:ad-unavailable' с причиной.

Пример

Показывает полноэкранный interstitial прямо сейчас, например между двумя уровнями игры (автоматическое управление рекламой).

Возвращает: ничего. Когда реклама закрывается, срабатывает appmint:ad-closed с {adType:'interstitial', earned:false}. Когда её нельзя показать, срабатывает appmint:ad-unavailable с {adType:'interstitial', reason}. Требуется: реклама включена и Полноэкранная реклама → Interstitial. Выберите Режим запуска → По запросу, если только ваша страница должна решать когда.

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(); }
});

Поставьте игру и её звук на паузу перед рекламой и возобновите при любом ответе:

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);

Откройте статью после рекламы одним Promise, который выполняется с true, если реклама была показана, и с false, если пропущена:

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); });

Примечания: Приложение выдерживает не менее 60 секунд между двумя interstitial; вызов в этом промежутке возвращает cooldown. isInterstitialAdReady() уже учитывает паузу, поэтому проверка сначала позволяет избежать события. Показывайте interstitial в естественных паузах, а не при каждом нажатии. В сборках Под контролем разработчика используйте AppMintAds.show('interstitial').

showRewardedAd мост#

window.WebToApk.showRewardedAd()

Показать вознаграждаемую рекламу СЕЙЧАС (по инициативе пользователя — единственный способ показа вознаграждаемой рекламы, соответствующий политике). Сочетайте с режимом запуска «По запросу» в мастере, чтобы получить вознаграждаемую рекламу БЕЗ какой-либо автоматической рекламы при открытии приложения/навигации. Награда доставляется странице через deliverRewardToPage(): window.addEventListener('appmint:reward', e => { /* e.detail = {type,amount} */ }) Когда реклама закрывается, срабатывает 'appmint:ad-closed' с {adType:'rewarded', earned} — earned равно false, если пользователь вышел раньше, поэтому в этом случае ничего не начисляйте. Если сейчас показать нельзя — пропуск + срабатывает 'appmint:ad-unavailable' с причиной.

Пример

Показывает вознаграждаемую рекламу прямо сейчас по собственному нажатию пользователя «Смотреть рекламу» (автоматическое управление рекламой).

Возвращает: ничего. Результат приходит позже в виде событий: appmint:reward {type, amount}, когда рекламу досмотрели до конца, appmint:ad-closed {adType:'rewarded', earned}, когда она закрывается, или appmint:ad-unavailable {adType:'rewarded', reason}, когда показать ничего не удалось. Требуется: реклама включена, Полноэкранная реклама → Вознаграждаемая реклама и Режим запуска → По запросу, чтобы ничего не показывалось само.

Начисляйте награду только в обработчике награды, а не при нажатии:

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();
});

Те же три момента вызывают и хуки страницы, если вы предпочитаете по функции на каждый (каждый получает тот же объект, что и 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); };

Оберните вызов и его события в один Promise, чтобы каждая кнопка «Смотреть рекламу» читалась как обычный асинхронный вызов:

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.');
  });
});

Примечания: Вызывайте только из обработчика клика. Никогда не вызывайте из таймера, загрузки страницы или смены маршрута и никогда не прячьте за этим основную функцию. not_loaded тоже начинает новую загрузку, поэтому следующее нажатие обычно срабатывает. В сборках Управление рекламой → Под контролем разработчика используйте вместо этого AppMintAds.show('rewarded'), который отвечает Promise.