ساخت APK رایگان

API پل جاوااسکریپت

صندوق پوش و مخاطبان

پیام‌هایی که سازنده از یک داشبورد فرستاده و روی دستگاه نگه داشته می‌شوند تا صفحه بتواند تاریخچه نشان دهد، نه فقط یک اعلان بازشو - و شناسه‌ی کاربر واردشده و برچسب‌هایی که به مرکز ارسال اجازه می‌دهند یک کاربر یا یک برچسب را هدف بگیرد و اتوماسیون‌هایی مانند یادآوری سبد خرید رهاشده را اجرا کند.

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

نکته‌ها: پیام‌های مسیر خودشان تغییر واقعی نشانی‌ها را دنبال می‌کنند (بارگذاری صفحه، مسیرهای history.pushState، #/hash)؛ فقط برای نماهایی که نشانی تغییر نمی‌کند، مسیر را گزارش کنید. رویدادها جایی شمرده یا ذخیره نمی‌شوند؛ فقط تعیین می‌کنند پیام نمایش داده شود یا نه.

clearPushInbox پل#

window.WebToApk.clearPushInbox(): Boolean

صندوق ورودی پوش را خالی می‌کند (نوار اعلان‌ها و تاریخچه‌ی سرور دست‌نخورده می‌مانند).

نمونه

صندوق ورودی پوش را روی این گوشی خالی می‌کند.

مقدار بازگشتی: 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 همه یا هیچ است). عمداً بازگشت‌پذیر است: مورد پنهان می‌شود، نابود نمی‌شود، پس رابط می‌تواند تا چند ثانیه بعد از کشیدن «واگرد» ارائه دهد. فقط محلی: اعلان نوار اعلان‌ها و تاریخچه‌ی سازنده دست‌نخورده می‌مانند.

نمونه

یک پیام را از صندوق ورودی پوش پنهان می‌کند (کشیدن به کنار). با این روش می‌توان آن را برگرداند: restorePushMessage(id).

مقدار بازگشتی: true اگر پیام پنهان شد؛ false اگر شناسه ناشناخته است یا قبلاً پنهان شده بود. پیش‌نیاز: هنگام ساخت 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();

نکته‌ها: فقط شناسه و نام نشان داده می‌شود؛ خود پیام هنگام نمایش از طریق رویداد appmint:inapp به صفحه می‌رسد (ببینید setInAppPresentation).

getPushInbox پل#

window.WebToApk.getPushInbox(): String

صندوق AppMint Push (پیام‌های ارسال‌شده توسط سازنده، جدیدترین اول، حداکثر 50): آرایه‌ی JSON از {id, title, body, imageUrl, link, receivedAt, read}. همیشه حقیقت کامل فعلی است - برنامه‌ای که دیر بالا بیاید با از دست دادن رویدادهای زنده‌ی `appmint:push` چیزی را از دست نمی‌دهد.

نمونه

صندوق ورودی درون‌برنامه‌ای پیام‌های پوشی را که با AppMint Push فرستاده‌اید می‌خواند: جدیدترین اول، تا 50 مورد، بدون آن‌هایی که کاربر کنار زده است.

مقدار بازگشتی: یک رشتهی JSON، همزمان. آن را parse کنید. هر مورد: { id, title, body, imageUrl, link, receivedAt, read } (receivedAt به epoch ms؛ 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 پس بگیرید از صندوق ورودی و نوار اعلان‌ها ناپدید می‌شود. پیام‌هایی که به‌صورت «فقط پوش» (نوار اعلان‌ها) فرستاده شده‌اند به صندوق ورودی اضافه نمی‌شوند. همین detail در صورت تعریف به 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

آخرین شناسه‌ی کاربری که با setPushUser تنظیم شده («» وقتی خارج شده یا هرگز تنظیم نشده).

نمونه

شناسه کاربری‌ای را می‌خواند که این نصب آخرین بار به AppMint Push داده است با setPushUser.

مقدار بازگشتی: یک string، همزمان - شناسه‌ی ذخیره‌شده، یا '' وقتی کسی وارد نشده یا هرگز تنظیم نشده. نیازمندی: هنگام ساخت، AppMint Push را در مرحله‌ی یکپارچه‌سازی (مرحله 3) روشن کنید.

شناسه را فقط وقتی تغییر کرده تنظیم کنید (در هر شروع اپلیکیشن):

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

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

شناسه‌ی کهنه را پاک کنید وقتی نشست خودتان در حالی که اپلیکیشن بسته بود منقضی شده است:

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

یک پیام پوش را خوانده‌شده علامت می‌زند؛ «» بدهید تا همه‌ی پیام‌ها خوانده‌شده شوند.

نمونه

یک پیام صندوق پوش را خوانده‌شده علامت می‌زند، یا همه‌ی آن‌ها را وقتی بدهید ''.

مقدار بازگشتی: true اگر پیامی تغییر کرد؛ false اگر چیزی تغییر نکرد (شناسه‌ی ناشناخته، قبلاً خوانده شده یا 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

بازگرداندن dismiss: پیام را به جای خودش در صندوق برمی‌گرداند.

نمونه

dismissPushMessage(id) را برمی‌گرداند: پیام به جای قبلی‌اش در صندوق پوش برمی‌گردد.

مقدار بازگشتی: true اگر پیام بازیابی شد؛ false اگر شناسه ناشناخته است یا پنهان نشده بود. نیازمندی: هنگام ساخت، 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() پیام‌های پنهان‌شده را برنمی‌گرداند، پس شناسه و عنوان آن‌ها را خودتان نگه دارید:

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' پیامی را که اپلیکیشن همین حالا نشان می‌دهد هم می‌بندد. پیام‌های پوش کلید جداگانه‌ی خودشان را دارند، setPushPresentation.

setPushPresentation پل#

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

نمایش درون‌برنامه‌ای بومی پیام‌های پوش: «auto» (پیش‌فرض) به پوسته اجازه می‌دهد وقتی اپلیکیشن باز است بنر / مودال / پنل / کارت تمام‌صفحه‌ای را که سازنده انتخاب کرده رسم کند؛ «none» آن را برای همیشه خاموش می‌کند (ماندگار) - برای اپلیکیشن‌هایی که خودشان رسم می‌کنند. رویداد `appmint:push` و صندوق ورودی در هر دو حالت بی‌تأثیرند؛ پرچم `presented` در رویداد دریافتی می‌گوید پوسته آن را نشان داده یا نه.

نمونه

نمایش روی صفحه‌ی خود برنامه از پیام‌های AppMint Push را روشن ('auto'، پیش‌فرض) یا خاموش ('none') می‌کند - برای برنامه‌هایی که بنر خودشان را از رویداد 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. مرکز ارسال (Home > Notifications) می‌تواند به همه‌ی دارندگان یک تگ بفرستد و اتوماسیون تگ می‌تواند مدتی بعد از تنظیم تگ پیامی بفرستد - سبد رهاشده، مرحله‌ی تمام‌شده. مقدار خالی تگ را حذف می‌کند.

مقدار بازگشتی: true وقتی ذخیره شد؛ false وقتی کلید 1 تا 32 حرف، رقم یا _ نیست، مقدار از 128 نویسه بلندتر است، یا نصب بیش از 50 برچسب پیدا می‌کند (در این صورت چیزی ذخیره نمی‌شود). برچسب‌ها روی گوشی نگه داشته می‌شوند و کمی بعد (یا در اجرای بعدی اگر آفلاین باشد) به AppMint Push گزارش می‌شوند. نیازمندی: هنگام ساخت، AppMint Push را در مرحله‌ی یکپارچه‌سازی (مرحله 3) روشن کنید. هدف‌گیری با برچسب و اتوماسیون‌ها از 30 Sep 2026 به بعد به گوشی‌هایی می‌رسد که اپلیکیشن ساخته‌شده با AppMint را اجرا می‌کنند.

آنچه کاربر خریده یا انتخاب کرده را برچسب بزنید تا بتوانید فقط به «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
}

تگ‌های مختص کاربر را هنگام خروج از حساب پاک کنید، همراه با شناسه‌ی کاربر:

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) روشن کنید. هدف‌گیری با برچسب و اتوماسیون‌ها از 30 Sep 2026 به بعد به گوشی‌هایی می‌رسد که اپلیکیشن ساخته‌شده با AppMint را اجرا می‌کنند.

بعد از ورود، نمایه را با یک فراخوانی برچسب بزنید:

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») یا به همه‌ی دارندگان یک برچسب فرستاد. بعد از ورود با شناسه‌ی حساب ثابت خودتان آن را صدا بزنید؛ هنگام خروج «» بدهید. حروف، ارقام و _ @ . : + - (تا 128 نویسه). برای هر چیز دیگر false برمی‌گرداند و در اپلیکیشنی که از AppMint Push استفاده نمی‌کند (OneSignal / Firebase شخصی: چیزی ارسال نمی‌شود، 2026-10-03). هرگز نشانی ایمیل یا شماره تلفن ندهید؛ شناسه‌ی پایگاه داده‌ی خودتان را بدهید.

نمونه

به AppMint Push می‌گوید کدام یک از کاربران شما روی این گوشی وارد شده است، تا مرکز ارسال (خانه > اعلان‌ها) بتواند پیامی به همان یک کاربر بفرستد. بعد از ورود با شناسه‌ی حساب ثابت خودتان آن را صدا بزنید؛ هنگام خروج با '' صدا بزنید.

مقدار بازگشتی: true وقتی شناسه ذخیره شد؛ false برای شناسه‌ای با چیزی غیر از حروف، ارقام و _ @ . : + -، یا بلندتر از 128 نویسه (در این حالت چیزی ذخیره نمی‌شود). شناسه تا وقتی تغییرش دهید روی گوشی می‌ماند و این نصب لحظه‌ای بعد (یا در اجرای بعدی اگر آفلاین است) آن را به AppMint Push گزارش می‌دهد. پیش‌نیاز: هنگام ساخت AppMint Push را در مرحله‌ی یکپارچه‌سازی (مرحله‌ی 3) روشن کنید. ارسال به یک کاربر به گوشی‌هایی می‌رسد که برنامه‌ای ساخته‌شده با AppMint از 30 Sep 2026 به بعد دارند؛ ساخت‌های قدیمی‌تر همچنان پیام‌های ارسال‌شده به همه را می‌گیرند.

بعد از ورود و هنگام خروج - شناسه‌ی پایگاه داده‌ی خودتان را به کار ببرید، هرگز نشانی ایمیل یا شماره تلفن را:

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

شناسه را قابل‌قبول کنید وقتی ممکن است شامل نویسه‌های دیگری باشد (فاصله، /، #) و موارد نادری را که هنوز رد می‌شوند ثبت کنید:

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

نکته‌ها: اگر یک کاربر روی دو گوشی وارد شده باشد، پیام به هر دو می‌رسد. شناسه متعلق به این نصب است، نه به حساب - هنگام خروج باید پاک شود، وگرنه گوشی پیام‌های کاربر قبلی را همچنان می‌گیرد. setPushUser('') برچسب‌های تنظیم‌شده با setPushTag را پاک نمی‌کند؛ برچسب‌های مخصوص کاربر را هم هنگام خروج پاک کنید.

trackEvent پل#

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

پیام‌های درون‌برنامه‌ای: رویدادی را گزارش می‌کند که کمپین‌های سازنده می‌توانند بر اساس آن اجرا شوند (محرک «event»، همان نام). propsJson اختیاری و JSON است. نام `appmint:route` با {"path":"/x"} تغییر نمایی را گزارش می‌کند که URL نشان نمی‌دهد. برای نامی خارج از [A-Za-z0-9_.:-]{1,64} و بدون AppMint Push، false.

نمونه

به اپلیکیشن اطلاع می‌دهد که اتفاقی افتاده است («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' } }));

نکته‌ها: پیام‌های مسیر خودشان تغییر واقعی نشانی‌ها را دنبال می‌کنند (بارگذاری صفحه، مسیرهای history.pushState، #/hash)؛ فقط برای نماهایی که نشانی تغییر نمی‌کند، مسیر را گزارش کنید. رویدادها جایی شمرده یا ذخیره نمی‌شوند؛ فقط تعیین می‌کنند پیام نمایش داده شود یا نه.