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

JavaScript bridge API

Входящие push и аудитория

Сообщения, которые автор отправил из панели управления, хранятся на устройстве, чтобы страница могла показывать историю, а не только всплывающее уведомление в шторке, — а также id вошедшего пользователя и теги, которые позволяют центру рассылки целиться в одного пользователя или тег и запускать автоматизации, например напоминание о брошенной корзине.

AppMint.trackEvent помощник#

window.AppMint.trackEvent

Пример

Описано вместе с WebToApk.trackEvent — пример там показывает и этот.

Сообщает приложению, что что-то произошло («signed_up», «checkout_done», «level:3»). Сообщение в приложении, которое автор создал в центре рассылки AppMint Push с триггером «Когда происходит событие» и тем же названием, показывается сейчас (или после ожидания, выбранного автором).

Возвращает: true, когда событие принято; false для имени вне букв, цифр и _ . : - (до 64) и в приложении, собранном без AppMint Push. AppMint.trackEvent(name, props) — тот же вызов с props в виде объекта. Требуется: включённая опция AppMint Push на шаге «Интеграция» (шаг 3) при сборке. propsJson необязателен ('') и не сохраняется.

// After a purchase: the creator's "Thanks, rate us?" message is set to trigger on "purchase_done".
function onPurchaseDone(order) {
  if (window.AppMint && AppMint.trackEvent) {
    AppMint.trackEvent('purchase_done', { total: order.total });
  }
}

Низкоуровневый мост принимает свойства в виде JSON-строки (передайте '', если их нет):

function track(name, props) {
  if (!(window.WebToApk && window.WebToApk.trackEvent)) return false;   // a browser: nothing to show
  return WebToApk.trackEvent(name, props ? JSON.stringify(props) : '');
}
track('level:3');
track('tutorial_skipped', { step: 2 });

Смену экрана, которую не показывает адрес (вкладки, мастер, модальная «страница»), можно сообщить как маршрут, чтобы её увидели сообщения автора с условием «На странице». Страница также может отправить событие appmint:route с путём; приложение его слушает:

function showTab(name) {
  document.querySelectorAll('.tab').forEach(function (t) { t.hidden = t.id !== name; });
  if (window.AppMint && AppMint.trackEvent) AppMint.trackEvent('appmint:route', { path: '/' + name });
}

// Same thing, without touching the bridge at all:
window.dispatchEvent(new CustomEvent('appmint:route', { detail: { path: '/cart' } }));

Примечания: сообщения по маршрутам сами следуют за реальными изменениями URL (загрузки страниц, маршруты history.pushState, #/hash); сообщайте о маршруте только для экранов, при которых адрес не меняется. События нигде не подсчитываются и не хранятся; они лишь определяют, показывать ли сообщение.

clearPushInbox мост#

window.WebToApk.clearPushInbox(): Boolean

Очищает входящие push-сообщения (шторка и история на сервере не затрагиваются).

Пример

Очищает входящие push-сообщения на этом телефоне.

Возвращает: true при успехе; false при внутренней ошибке. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

document.getElementById('clear-inbox').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.clearPushInbox)) return;
  if (!confirm('Delete all messages? This cannot be undone.')) return;
  if (WebToApk.clearPushInbox()) renderInbox();
});

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

async function signOut() {
  await myAuth.signOut();
  if (window.WebToApk && window.WebToApk.clearPushInbox) {
    WebToApk.clearPushInbox();
  }
  document.getElementById('bell-count').textContent = '';
  showLoginScreen();
}

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

function paintClearRow() {
  const btn = document.getElementById('clear-inbox');
  if (!(window.WebToApk && window.WebToApk.clearPushInbox)) { btn.hidden = true; return; }
  const count = JSON.parse(WebToApk.getPushInbox() || '[]').length;
  btn.textContent = count ? 'Delete ' + count + ' messages' : 'No messages';
  btn.disabled = count === 0;
}

document.getElementById('clear-inbox').addEventListener('click', function () {
  if (WebToApk.clearPushInbox()) paintClearRow();
  else alert('Could not delete the messages. Please try again.');
});

paintClearRow();

Примечания: это необратимо — в отличие от dismissPushMessage, отмены нет. Уведомления в шторке и ваша история отправок в AppMint не затрагиваются. Новые сообщения по-прежнему приходят.

dismissPushMessage мост#

window.WebToApk.dismissPushMessage(id: String): Boolean

Убирает ОДНО сообщение из входящих — смахивание строки, нужное экрану входящих (clearPushInbox — всё или ничего). ОБРАТИМО по замыслу: запись скрывается, а не уничтожается, поэтому интерфейс может предложить «Отменить» в течение нескольких секунд после смахивания. Только локально: уведомление в шторке и история автора не затрагиваются.

Пример

Скрывает одно сообщение из входящих push-сообщений (смахиванием). Это можно отменить с помощью restorePushMessage(id).

Возвращает: true, если сообщение было скрыто; false, если id неизвестен или оно уже скрыто. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

Смахивание с отменой в течение 5 секунд:

function dismissRow(id, rowEl) {
  if (!(window.WebToApk && window.WebToApk.dismissPushMessage)) return;
  if (!WebToApk.dismissPushMessage(id)) return;
  rowEl.remove();

  const bar = document.getElementById('undo-bar');
  bar.hidden = false;
  const timer = setTimeout(function () { bar.hidden = true; }, 5000);
  document.getElementById('undo-btn').onclick = function () {
    clearTimeout(timer);
    bar.hidden = true;
    WebToApk.restorePushMessage(id);         // back in its old place
    renderInbox();
  };
}

Наведите порядок во входящих при открытии, скрыв прочитанные сообщения старше 30 дней:

function hideOldReadMessages() {
  if (!(window.WebToApk && window.WebToApk.dismissPushMessage)) return 0;
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
  let hidden = 0;
  JSON.parse(WebToApk.getPushInbox() || '[]').forEach(function (m) {
    if (m.read && m.receivedAt < cutoff && WebToApk.dismissPushMessage(m.id)) hidden++;
  });
  return hidden;
}

if (hideOldReadMessages() > 0) renderInbox();

Скройте строку настоящим смахиванием пальцем (потяните её более чем на 40% её ширины):

function makeSwipeable(rowEl, id) {
  let startX = null;
  rowEl.addEventListener('pointerdown', function (e) { startX = e.clientX; rowEl.setPointerCapture(e.pointerId); });
  rowEl.addEventListener('pointermove', function (e) {
    if (startX !== null) rowEl.style.transform = 'translateX(' + (e.clientX - startX) + 'px)';
  });
  rowEl.addEventListener('pointerup', function (e) {
    const dx = startX === null ? 0 : e.clientX - startX;
    startX = null;
    if (Math.abs(dx) > rowEl.offsetWidth * 0.4 && window.WebToApk && WebToApk.dismissPushMessage(id)) {
      rowEl.remove();
    } else {
      rowEl.style.transform = '';            // not far enough: snap back
    }
  });
}

Примечания: меняются только входящие на этом телефоне. Уведомление в шторке и ваша история отправок в AppMint остаются. Скрытые сообщения не возвращаются функцией getPushInbox().

getInAppCampaigns мост#

window.WebToApk.getInAppCampaigns(): String

Сообщения в приложении, которые хранит эта установка: JSON [{id, name}] («[]» без AppMint Push).

Пример

Перечисляет сообщения в приложении, которые сейчас хранятся в этой установке (кампании автора из центра рассылки AppMint Push, в аудиторию которых входит этот пользователь), в виде строки JSON [{"id": "...", "name": "..."}].

Возвращает: строку с массивом JSON; "[]", когда сообщений нет и в приложении, собранном без AppMint Push. Требуется: включённая опция AppMint Push на шаге «Интеграция» (шаг 3) при сборке. Список обновляется при каждом запуске приложения (и примерно раз в час на переднем плане).

var list = [];
if (window.WebToApk && window.WebToApk.getInAppCampaigns) {
  try { list = JSON.parse(WebToApk.getInAppCampaigns()); } catch (e) {}
}
console.log('in-app messages on this phone:', list.map(function (c) { return c.name; }));

Покажите отладочный список на собственном экране администратора:

function renderInAppDebug(el) {
  var raw = (window.WebToApk && WebToApk.getInAppCampaigns) ? WebToApk.getInAppCampaigns() : '[]';
  var items = JSON.parse(raw);
  el.innerHTML = items.length
    ? items.map(function (c) { return '<li>' + c.name + ' <code>' + c.id + '</code></li>'; }).join('')
    : '<li>No in-app messages for this install</li>';
}
renderInAppDebug(document.getElementById('inapp-debug'));

Пропустите собственную приветственную подсказку, если автор уже запускает сообщение с таким названием:

function hasCampaign(name) {
  try { return JSON.parse(WebToApk.getInAppCampaigns()).some(function (c) { return c.name === name; }); }
  catch (e) { return false; }
}
if (!hasCampaign('Welcome tour')) showMyOwnWelcome();

Примечания: доступны только id и имя; само сообщение доходит до страницы через событие appmint:inapp при показе (см. setInAppPresentation).

getPushInbox мост#

window.WebToApk.getPushInbox(): String

Входящие AppMint Push (сообщения от автора, сначала новые, не более 50): массив JSON из {id, title, body, imageUrl, link, receivedAt, read}. Всегда полная актуальная картина — приложение, которое смонтировалось поздно, ничего не теряет из-за пропущенных живых событий `appmint:push`.

Пример

Читает входящие в приложении push-сообщения, которые вы отправили через AppMint Push: сначала новые, до 50, без тех, что пользователь закрыл.

Возвращает: JSON-строку, синхронно. Разберите её. Каждый элемент: { id, title, body, imageUrl, link, receivedAt, read } (receivedAt в epoch мс; imageUrl и link равны '', если у сообщения их нет), а также необязательные поля оформления, если вы использовали шаблон в AppMint: template, accent, iconUrl, buttons: [{label, link}], badge, inapp, countdownAt, progress, lines, collapse. '[]', когда пусто. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке; без этого входящие всегда пусты.

Отрисуйте входящие и обновляйте их вживую с помощью appmint:push:

function loadInbox() {
  if (!(window.WebToApk && window.WebToApk.getPushInbox)) return [];
  try { return JSON.parse(WebToApk.getPushInbox() || '[]'); } catch (e) { return []; }
}

function renderInbox() {
  const inbox = loadInbox();
  const unread = inbox.filter(function (m) { return !m.read; }).length;
  document.getElementById('bell-count').textContent = unread ? String(unread) : '';
  const ul = document.getElementById('inbox');
  ul.innerHTML = '';
  inbox.forEach(function (m) {
    const li = document.createElement('li');
    li.className = m.read ? 'read' : 'unread';
    li.textContent = m.title + ' — ' + m.body;
    li.onclick = function () {
      WebToApk.markPushRead(m.id);
      if (m.link) location.href = m.link;
      renderInbox();
    };
    ul.appendChild(li);
  });
}

// While the app is open:
window.addEventListener('appmint:push', function (e) {
  // { type: 'received', message: {id, title, body, ...}, presented: true|false }
  // { type: 'revoked', id }  — you retracted the message
  // presented = the app already showed it on screen (see setPushPresentation)
  if (e.detail.type === 'received' && !e.detail.presented) showBanner(e.detail.message.title);
  renderInbox();
});

renderInbox();

Обновляйте счётчик непрочитанных на колокольчике всякий раз, когда пользователь возвращается в приложение (сообщения, пришедшие в фоне, уже во входящих):

function unreadCount() {
  if (!(window.WebToApk && window.WebToApk.getPushInbox)) return 0;
  try {
    return JSON.parse(WebToApk.getPushInbox() || '[]').filter(function (m) { return !m.read; }).length;
  } catch (e) { return 0; }
}

function paintBell() {
  const n = unreadCount();
  const badge = document.getElementById('bell-count');
  badge.textContent = n > 9 ? '9+' : (n ? String(n) : '');
  badge.hidden = n === 0;
}

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState === 'visible') paintBell();
});
paintBell();

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

function renderCard(m) {
  const card = document.createElement('article');
  if (m.accent) card.style.borderLeft = '4px solid ' + m.accent;
  if (m.imageUrl) {
    const img = document.createElement('img');
    img.src = m.imageUrl; img.alt = '';
    card.appendChild(img);
  }
  const h = document.createElement('h3');
  h.textContent = m.title;
  const p = document.createElement('p');
  p.textContent = m.body;
  const time = document.createElement('time');
  time.textContent = new Date(m.receivedAt).toLocaleString();
  card.append(h, p, time);
  (m.buttons || []).forEach(function (b) {
    const btn = document.createElement('button');
    btn.textContent = b.label;
    btn.onclick = function () { WebToApk.markPushRead(m.id); if (b.link) location.href = b.link; };
    card.appendChild(btn);
  });
  return card;
}

if (window.WebToApk && window.WebToApk.getPushInbox) {
  const list = document.getElementById('inbox');
  JSON.parse(WebToApk.getPushInbox() || '[]').forEach(function (m) { list.appendChild(renderCard(m)); });
}

Примечания: входящие хранятся на телефоне, поэтому читайте их при открытии экрана — события приходят только пока приложение на экране. Сообщение, которое вы отозвали в AppMint, исчезает из входящих и из шторки. Сообщения, отправленные как «только push» (шторка), во входящие не добавляются. Те же данные передаются и в window.onAppMintPush(detail), если вы его определили.

getPushTags мост#

window.WebToApk.getPushTags(): String

Теги этой установки в виде строки с JSON-объектом, например {"plan":"pro"}.

Пример

Читает метки AppMint Push этой установки.

Возвращает: JSON-строку синхронно — объект с текстовыми значениями, например '{"plan":"pro","cart":"open"}'; '{}', если тегов нет. Разберите её. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

Прочитайте один тег:

function pushTags() {
  if (!(window.WebToApk && window.WebToApk.getPushTags)) return {};
  try { return JSON.parse(WebToApk.getPushTags() || '{}'); } catch (e) { return {}; }
}

const plan = pushTags().plan || 'free';

Устанавливайте метку, только когда она меняется — каждый вызов setPushTag заставляет установку обращаться к AppMint Push, поэтому пропускайте те, что ничего не меняют:

function ensureTag(key, value) {
  if (!(window.WebToApk && window.WebToApk.getPushTags)) return;
  const tags = JSON.parse(WebToApk.getPushTags() || '{}');
  if ((tags[key] || '') !== value) WebToApk.setPushTag(key, value);
}

ensureTag('plan', user.isPro ? 'pro' : 'free');

Выведите их на экране отладки, пока тестируете отправку по метке:

const list = document.getElementById('push-tags');
if (window.WebToApk && window.WebToApk.getPushTags) {
  const tags = JSON.parse(WebToApk.getPushTags() || '{}');
  list.textContent = Object.keys(tags).length
    ? Object.keys(tags).map(function (k) { return k + ' = ' + tags[k]; }).join('\n')
    : 'No tags yet';
}

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

getPushUser мост#

window.WebToApk.getPushUser(): String

id пользователя, последний раз установленный через setPushUser («» после выхода или если он не задавался).

Пример

Читает идентификатор пользователя, который эта установка в последний раз передала AppMint Push через setPushUser.

Возвращает: string синхронно — сохранённый id или '', если никто не вошёл или он не задавался. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

Устанавливайте id только при изменении (при каждом запуске приложения):

function syncPushUser(currentUserId) {
  if (!(window.WebToApk && window.WebToApk.getPushUser)) return;
  const want = currentUserId || '';
  if (WebToApk.getPushUser() !== want) WebToApk.setPushUser(want);
}

syncPushUser(localStorage.getItem('userId'));

Очистите устаревший id, если ваша собственная сессия истекла, пока приложение было закрыто:

async function checkSession() {
  const session = await myBackend.currentSession();       // null when expired
  if (!session && window.WebToApk && window.WebToApk.getPushUser && WebToApk.getPushUser() !== '') {
    WebToApk.setPushUser('');                              // stop personal messages to this phone
  }
}

checkSession();

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

const info = document.getElementById('push-debug');
if (window.WebToApk && window.WebToApk.getPushUser) {
  const id = WebToApk.getPushUser();
  info.textContent = id ? 'Push user: ' + id : 'Push user: nobody signed in';
} else {
  info.textContent = 'Not inside the app';
}

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

markPushRead мост#

window.WebToApk.markPushRead(id: String): Boolean

Отмечает одно push-сообщение прочитанным; передайте «», чтобы отметить прочитанными все.

Пример

Отмечает одно сообщение во входящих push как прочитанное или все сразу, если передать ''.

Возвращает: true, если сообщение изменилось; false, если ничего не изменилось (неизвестный id, уже прочитано или AppMint Push не включён). Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

function openMessage(id) {
  if (!(window.WebToApk && window.WebToApk.markPushRead)) return;
  WebToApk.markPushRead(id);                 // one message
  updateBellCount();
}

document.getElementById('mark-all-read').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.markPushRead)) return;
  WebToApk.markPushRead('');                 // every message
  updateBellCount();
});

function updateBellCount() {
  const inbox = JSON.parse(WebToApk.getPushInbox() || '[]');
  const unread = inbox.filter(function (m) { return !m.read; }).length;
  document.getElementById('bell-count').textContent = unread ? String(unread) : '';
}

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

if (window.WebToApk && window.WebToApk.markPushRead) {
  const seen = new IntersectionObserver(function (entries) {
    entries.forEach(function (entry) {
      if (!entry.isIntersecting) return;
      const id = entry.target.dataset.msgId;
      seen.unobserve(entry.target);
      if (WebToApk.markPushRead(id)) {       // true only when it was unread
        entry.target.classList.replace('unread', 'read');
        updateBellCount();
      }
    });
  }, { threshold: 0.6 });

  document.querySelectorAll('#inbox li.unread').forEach(function (li) { seen.observe(li); });
}

Сбрасывайте значок при открытии экрана входящих и перерисовывайте только если что-то действительно было непрочитанным:

function openInboxScreen() {
  showScreen('inbox');
  if (!(window.WebToApk && window.WebToApk.markPushRead)) return;
  const changed = WebToApk.markPushRead('');  // false: nothing was unread
  if (changed) {
    document.getElementById('bell-count').textContent = '';
    renderInbox();
  }
}

Примечания: состояние «прочитано» хранится только на этом телефоне. Уведомление в шторке оно не удаляет.

restorePushMessage мост#

window.WebToApk.restorePushMessage(id: String): Boolean

Отмена скрытия: возвращает сообщение на прежнее место во входящих.

Пример

Отменяет dismissPushMessage(id): сообщение возвращается во входящие push на прежнее место.

Возвращает: true, если сообщение восстановлено; false, если id неизвестен или сообщение не было скрыто. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

let lastDismissed = null;

function onSwipe(id) {
  if (!(window.WebToApk && window.WebToApk.dismissPushMessage)) return;
  if (WebToApk.dismissPushMessage(id)) {
    lastDismissed = id;
    showUndo();
    renderInbox();
  }
}

document.getElementById('undo-btn').addEventListener('click', function () {
  if (!lastDismissed || !window.WebToApk) return;
  if (WebToApk.restorePushMessage(lastDismissed)) renderInbox();
  lastDismissed = null;
});

Отмените несколько смахиваний сразу: запоминайте каждое сообщение, скрытое на этом экране, и возвращайте их все:

const hiddenThisVisit = [];

function hide(id) {
  if (window.WebToApk && WebToApk.dismissPushMessage(id)) {
    hiddenThisVisit.push(id);
    document.getElementById('undo-all').hidden = false;
    renderInbox();
  }
}

document.getElementById('undo-all').addEventListener('click', function () {
  if (!(window.WebToApk && window.WebToApk.restorePushMessage)) return;
  hiddenThisVisit.splice(0).forEach(function (id) { WebToApk.restorePushMessage(id); });
  this.hidden = true;
  renderInbox();
});

Предложите список «Недавно скрытые», из которого пользователь сможет восстановить сообщения позже. getPushInbox() не возвращает скрытые сообщения, поэтому храните их id и заголовки самостоятельно:

function loadHidden() {
  try { return JSON.parse(localStorage.getItem('hiddenMsgs') || '[]'); } catch (e) { return []; }
}

function renderHidden() {
  const ul = document.getElementById('recently-hidden');
  ul.innerHTML = '';
  loadHidden().forEach(function (h) {
    const li = document.createElement('li');
    li.textContent = h.title + ' ';
    const btn = document.createElement('button');
    btn.textContent = 'Restore';
    btn.onclick = function () {
      WebToApk.restorePushMessage(h.id);     // false: retracted, cleared or already back
      const rest = loadHidden().filter(function (x) { return x.id !== h.id; });
      try { localStorage.setItem('hiddenMsgs', JSON.stringify(rest)); } catch (e) {}
      renderHidden();
      renderInbox();
    };
    li.appendChild(btn);
    ul.appendChild(li);
  });
}

Примечания: сообщение, удалённое через clearPushInbox() или отозванное вами в AppMint, восстановить нельзя.

setInAppPresentation мост#

window.WebToApk.setInAppPresentation(mode: String): Boolean

Показ сообщений в приложении: "auto" (по умолчанию) позволяет оболочке отрисовать дизайн автора; "none" (сохраняется) передаёт каждое сообщение странице как `appmint:inapp` {type:'shown', presented:false, message}, чтобы она отрисовала его сама.

Пример

Выбирает, кто рисует сообщение в приложении (кампанию из центра рассылки AppMint Push), когда срабатывает его триггер: приложение ('auto', по умолчанию: баннер, карточка по центру, нижняя панель или полноэкранный дизайн автора) или ваша собственная страница ('none').

Возвращает: true, когда режим сохранён (он остаётся на телефоне, пока не изменён); false для любого другого значения и в приложении, собранном без AppMint Push. Требуется: включённая опция AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

Каждое сообщение в приложении вызывает событие appmint:inapp (и вызывает window.onAppMintInApp(detail), если вы его определили) с type 'shown', 'clicked', 'dismissed' или 'answered' (ответ в виде звезды или оценки 0–10, с question и value):

window.addEventListener('appmint:inapp', function (e) {
  var d = e.detail;   // { type, id, name, template?, presented?, message?, button?, link?, question?, value? }
  if (d.type === 'shown') analytics('inapp_shown', d.name);
  if (d.type === 'clicked') analytics('inapp_clicked', d.name + ' #' + d.button);
  if (d.type === 'dismissed') analytics('inapp_closed', d.name);
  if (d.type === 'answered') analytics('inapp_answer', d.name + ' ' + d.question + '=' + d.value);
});

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

if (window.WebToApk && WebToApk.setInAppPresentation) WebToApk.setInAppPresentation('none');

window.onAppMintInApp = function (d) {
  if (d.type !== 'shown' || d.presented) return;   // presented = the app already drew it
  var m = d.message;                                // { title, body, imageUrl, link, inapp, accent?, buttons?, emoji?, question?, lines? }
  showMyDialog(m.title, m.body, (m.buttons || []).map(function (b) {
    return { label: b.label, onClick: function () { if (b.link) location.href = b.link; } };
  }));
};

Верните выбор приложению (например, с переключателя в настройках):

document.getElementById('native-popups').addEventListener('change', function (e) {
  if (window.WebToApk && WebToApk.setInAppPresentation) {
    WebToApk.setInAppPresentation(e.target.checked ? 'auto' : 'none');
  }
});

Примечания: с 'none' сообщение всё равно считается показанным (действуют ограничения частоты) в момент передачи странице. Установка 'none' также закрывает сообщение, которое приложение показывает сейчас. У push-сообщений есть собственный переключатель, setPushPresentation.

setPushPresentation мост#

window.WebToApk.setPushPresentation(mode: String): Boolean

Показ push-сообщений в приложении: «auto» (по умолчанию) позволяет оболочке рисовать баннер / модальное окно / нижнюю панель / полноэкранную карточку, выбранные автором, пока приложение открыто; «none» отключает это навсегда (сохраняется) — для приложений, которые рисуют сами. Событие `appmint:push` и входящие в любом случае не затрагиваются; флаг `presented` полученного события говорит, показала ли его оболочка.

Пример

Включает ('auto', по умолчанию) или отключает ('none') собственный показ сообщений AppMint Push на экране приложения — для приложений, которые рисуют собственный баннер по событию appmint:push.

Возвращает: true, если режим сохранён; false для любого значения, кроме 'auto' или 'none'. Выбор сохраняется на телефоне и действует до изменения. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке.

Нарисуйте собственный баннер вместо баннера оболочки:

if (window.WebToApk && window.WebToApk.setPushPresentation) {
  WebToApk.setPushPresentation('none');       // 'auto' to give it back to the shell
}

window.addEventListener('appmint:push', function (e) {
  const d = e.detail;
  // { type: 'received', message: {...}, presented: false } — presented = the shell already showed it
  if (d.type === 'received' && !d.presented) showMyBanner(d.message.title, d.message.body, d.message.link);
  if (d.type === 'revoked') hideMyBanner(d.id);
});

Позвольте пользователю отключить всплывающие окна на экране ваших настроек. Геттера нет, поэтому храните собственную копию выбора, чтобы отрисовать переключатель:

const toggle = document.getElementById('show-popups');
let saved = null;
try { saved = localStorage.getItem('pushPopups'); } catch (e) {}
toggle.checked = saved !== 'none';

toggle.addEventListener('change', function () {
  if (!(window.WebToApk && window.WebToApk.setPushPresentation)) return;
  const mode = toggle.checked ? 'auto' : 'none';
  if (WebToApk.setPushPresentation(mode)) {
    try { localStorage.setItem('pushPopups', mode); } catch (e) {}
  }
});

С 'auto' узнайте, что пользователь сделал с сообщением, показанным приложением на экране: opened — когда он нажал на него (action равно -1 для самого сообщения, иначе индекс кнопки), closed — когда закрыл без нажатия:

window.addEventListener('appmint:push', function (e) {
  const d = e.detail;
  if (d.type === 'opened') {
    // the message is already marked read in the inbox
    track('push_opened', { id: d.id, button: d.action, link: d.link });
    updateBellCount();
  } else if (d.type === 'closed') {
    track('push_closed', { id: d.id });
  }
});

Примечания: с 'auto' сообщение, отправленное в стиле «в приложении», показывается поверх приложения, пока оно открыто (и тогда не дублируется в шторке). С 'none' оболочка его никогда не рисует; входящие (getPushInbox) и событие appmint:push работают одинаково в обоих случаях. Установка 'none' также закрывает сообщение, которое оболочка показывает сейчас.

setPushTag мост#

window.WebToApk.setPushTag(key: String, value: String): Boolean

Устанавливает один тег AppMint Push (ключ: буквы, цифры, _ до 32 символов; значение до 128) — центр рассылки может целиться в «plan = pro», а автоматизация может сработать через заданное время после установки тега (брошенная корзина: установите cart=open, снимите при оформлении заказа). Пустое значение удаляет тег. До 50 тегов на установку.

Пример

Задаёт одну метку AppMint Push для этой установки, например plan = pro или city = pune. Центр рассылки (Главная > Уведомления) может отправлять всем с меткой, а автоматизация по метке может отправить сообщение через заданное время после установки метки — брошенная корзина, пройденный уровень. Пустое значение удаляет метку.

Возвращает: true при сохранении; false, если ключ не состоит из 1–32 букв, цифр или _, значение длиннее 128 символов или у установки стало бы больше 50 тегов (тогда ничего не сохраняется). Теги хранятся на телефоне и передаются в AppMint Push чуть позже (или при следующем запуске, если нет сети). Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке. Таргетинг по тегам и автоматизации работают на телефонах с приложением, собранным в AppMint, начиная с 30 сентября 2026 года.

Пометьте тегом то, что пользователь купил или выбрал, чтобы отправлять только тем, у кого «plan = pro»:

function pushTag(key, value) {
  if (!(window.WebToApk && window.WebToApk.setPushTag)) return false;
  return WebToApk.setPushTag(key, value);
}

pushTag('plan', user.isPro ? 'pro' : 'free');
pushTag('city', user.city.toLowerCase());

Брошенная корзина: установите тег, когда в корзине есть товары, и снимите при оформлении заказа. В центре рассылки добавьте автоматизацию по тегу cart с задержкой (скажем, 60 минут); телефон получит напоминание, только если к моменту срабатывания cart всё ещё установлен:

function onCartChanged(items) {
  if (!(window.WebToApk && window.WebToApk.setPushTag)) return;
  // Setting 'open' again while it is already 'open' does not restart the automation's clock.
  WebToApk.setPushTag('cart', items.length > 0 ? 'open' : '');
}

function onCheckoutComplete() {
  if (window.WebToApk && window.WebToApk.setPushTag) WebToApk.setPushTag('cart', '');   // bought: no reminder
}

Очищайте пользовательские метки при выходе из аккаунта вместе с id пользователя:

function signOutOfPush() {
  if (!(window.WebToApk && window.WebToApk.setPushUser)) return;
  WebToApk.setPushUser('');
  WebToApk.setPushTag('plan', '');
  WebToApk.setPushTag('cart', '');
}

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

setPushTags мост#

window.WebToApk.setPushTags(json: String): Boolean

Задаёт сразу несколько меток из строки с объектом JSON; значения "" удаляют.

Пример

Устанавливает сразу несколько тегов AppMint Push из строки с JSON-объектом, например '{"plan":"pro","team":"red"}'. Значение "" удаляет этот тег; теги, которые вы не назвали, сохраняются.

Возвращает: true при сохранении; false, если строка не является JSON-объектом, любой ключ не состоит из 1–32 букв, цифр или _, любое значение длиннее 128 символов или у установки стало бы больше 50 тегов — тогда не сохраняется ничего. Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке. Таргетинг по тегам и автоматизации работают на телефонах с приложением, собранным в AppMint, начиная с 30 сентября 2026 года.

После входа пометьте профиль тегами одним вызовом:

function tagProfile(profile) {
  if (!(window.WebToApk && window.WebToApk.setPushTags)) return false;
  return WebToApk.setPushTags(JSON.stringify({
    plan: profile.plan,                   // 'free' | 'pro'
    level: String(profile.level),         // values are text: turn numbers into strings
    team: profile.team || ''              // '' removes the tag
  }));
}

При выходе удалите их все (прочитайте текущие теги, отправьте каждый обратно как ''):

function clearPushTags() {
  if (!(window.WebToApk && window.WebToApk.getPushTags)) return;
  const tags = JSON.parse(WebToApk.getPushTags() || '{}');
  Object.keys(tags).forEach(function (k) { tags[k] = ''; });
  WebToApk.setPushTags(JSON.stringify(tags));
  WebToApk.setPushUser('');
}

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

function onLevelComplete(level, score) {
  if (!(window.WebToApk && window.WebToApk.setPushTags)) return;
  WebToApk.setPushTags(JSON.stringify({
    level_done: String(level),            // a new value each level, so the automation can fire again
    last_score: String(score)
  }));
}

Примечания: один вызов — одно изменение: сохраняется всё или ничего. Установка передаёт новые теги в AppMint Push чуть позже (или при следующем запуске, если нет сети).

setPushUser мост#

window.WebToApk.setPushUser(id: String): Boolean

Аудитория AppMint Push: сообщает центру рассылки автора, КТО владелец этой установки, чтобы сообщение можно было отправить одному вошедшему пользователю («u_42») или всем с определённым тегом. Вызовите после входа со своим постоянным id аккаунта; при выходе передайте "". Буквы, цифры и символы _ @ . : + - (до 128). Для всего остального возвращает false, так же как и в приложении без AppMint Push (OneSignal / собственный Firebase: ничего не отправляется, 2026-10-03). Никогда не передавайте адрес электронной почты или номер телефона — используйте id из своей базы данных.

Пример

Сообщает AppMint Push, кто из ВАШИХ пользователей вошёл на этом телефоне, чтобы центр рассылки (Главная > Уведомления) мог отправить сообщение именно ему. Вызовите после входа со своим постоянным id аккаунта; при выходе вызовите с ''.

Возвращает: true, когда id сохранён; false для id с чем-либо, кроме букв, цифр и _ @ . : + -, или длиннее 128 символов (тогда ничего не сохраняется). Id хранится на телефоне, пока вы его не смените, и эта установка сообщает его в AppMint Push чуть позже (или при следующем запуске, если нет сети). Требуется: включите AppMint Push на шаге «Интеграция» (шаг 3) при сборке. Отправка конкретному пользователю доходит до телефонов с приложением, собранным в AppMint с 30 сентября 2026 г.; более старые сборки по-прежнему получают сообщения, отправленные всем.

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

function pushUser(id) {
  if (!(window.WebToApk && window.WebToApk.setPushUser)) return false;   // browser, or an older build
  return WebToApk.setPushUser(id);
}

async function signIn(email, password) {
  const user = await myBackend.signIn(email, password);   // your own sign-in
  pushUser(user.id);                                      // e.g. 'u_42' or a UUID
}

async function signOut() {
  await myBackend.signOut();
  pushUser('');                                           // this phone is nobody's now
}

Supabase: отслеживайте сессию, чтобы восстановленная сессия учитывалась так же, как новый вход:

supabase.auth.onAuthStateChange(function (event, session) {
  if (!(window.WebToApk && window.WebToApk.setPushUser)) return;
  const id = session ? session.user.id : '';     // the auth UUID: letters, digits and '-'
  if (WebToApk.getPushUser() !== id) WebToApk.setPushUser(id);
});

Сделайте id допустимым, если он может содержать другие символы (пробелы, /, #), и записывайте в лог редкие id, которые всё равно отклонены:

function pushUserIdFrom(rawId) {
  return String(rawId).trim().replace(/[^A-Za-z0-9_@.:+\-]/g, '_').slice(0, 128);
}

if (window.WebToApk && window.WebToApk.setPushUser) {
  const ok = WebToApk.setPushUser(pushUserIdFrom(currentUser.id));
  if (!ok) console.warn('push user id refused', currentUser.id);
}

Примечания: один и тот же пользователь, вошедший на двух телефонах, получит сообщения на обоих. id относится к этой установке, а не к аккаунту — при выходе его нужно очищать, иначе телефон продолжит получать сообщения предыдущего пользователя. setPushUser('') не очищает теги, установленные через setPushTag; при выходе очищайте и теги конкретного пользователя.

trackEvent мост#

window.WebToApk.trackEvent(name: String, propsJson: String): Boolean

Сообщения в приложении: сообщает о событии, по которому могут срабатывать кампании автора (триггер «event» с тем же именем). propsJson необязателен (JSON). Имя `appmint:route` с {"path":"/x"} сообщает о смене экрана, которую URL не показывает. False для имени вне [A-Za-z0-9_.:-]{1,64} и без AppMint Push.

Пример

Сообщает приложению, что что-то произошло («signed_up», «checkout_done», «level:3»). Сообщение в приложении, которое автор создал в центре рассылки AppMint Push с триггером «Когда происходит событие» и тем же названием, показывается сейчас (или после ожидания, выбранного автором).

Возвращает: true, когда событие принято; false для имени вне букв, цифр и _ . : - (до 64) и в приложении, собранном без AppMint Push. AppMint.trackEvent(name, props) — тот же вызов с props в виде объекта. Требуется: включённая опция AppMint Push на шаге «Интеграция» (шаг 3) при сборке. propsJson необязателен ('') и не сохраняется.

// After a purchase: the creator's "Thanks, rate us?" message is set to trigger on "purchase_done".
function onPurchaseDone(order) {
  if (window.AppMint && AppMint.trackEvent) {
    AppMint.trackEvent('purchase_done', { total: order.total });
  }
}

Низкоуровневый мост принимает свойства в виде JSON-строки (передайте '', если их нет):

function track(name, props) {
  if (!(window.WebToApk && window.WebToApk.trackEvent)) return false;   // a browser: nothing to show
  return WebToApk.trackEvent(name, props ? JSON.stringify(props) : '');
}
track('level:3');
track('tutorial_skipped', { step: 2 });

Смену экрана, которую не показывает адрес (вкладки, мастер, модальная «страница»), можно сообщить как маршрут, чтобы её увидели сообщения автора с условием «На странице». Страница также может отправить событие appmint:route с путём; приложение его слушает:

function showTab(name) {
  document.querySelectorAll('.tab').forEach(function (t) { t.hidden = t.id !== name; });
  if (window.AppMint && AppMint.trackEvent) AppMint.trackEvent('appmint:route', { path: '/' + name });
}

// Same thing, without touching the bridge at all:
window.dispatchEvent(new CustomEvent('appmint:route', { detail: { path: '/cart' } }));

Примечания: сообщения по маршрутам сами следуют за реальными изменениями URL (загрузки страниц, маршруты history.pushState, #/hash); сообщайте о маршруте только для экранов, при которых адрес не меняется. События нигде не подсчитываются и не хранятся; они лишь определяют, показывать ли сообщение.