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); сообщайте о маршруте только для экранов, при которых адрес не меняется. События нигде не подсчитываются и не хранятся; они лишь определяют, показывать ли сообщение.