أنماط الإشعارات الشائعة

سنلقي نظرة على بعض أنماط التنفيذ الشائعة للإعلانات الفورية على الإنترنت.

يشمل ذلك استخدام بعض واجهات برمجة التطبيقات المختلفة المتوفرة في مشغِّل الخدمات.

حدث إغلاق الإشعار

رأينا في القسم الأخير كيف يمكننا الاستماع إلى أحداث notificationclick.

هناك أيضًا حدث notificationclose يتم استدعاؤه إذا رفض المستخدم أحد الإشعارات (أي بدلاً من النقر على الإشعار، ينقر المستخدم فوق الرمز المتقاطع أو يمرر سريعًا غير متاح للإشعارات).

يُستخدَم هذا الحدث عادةً في الإحصاءات لتتبُّع تفاعل المستخدمين من خلال الإشعارات.

self.addEventListener('notificationclose', function (event) {
  const dismissedNotification = event.notification;

  const promiseChain = notificationCloseAnalytics();
  event.waitUntil(promiseChain);
});

إضافة بيانات إلى إشعار

عند تلقي رسالة فورية، من الشائع أن يكون لديك بيانات يكون مفيدًا إذا نقر المستخدم على الإشعار. على سبيل المثال، قد يستخدم عنوان URL يجب فتحها عند النقر على أحد الإشعارات.

تتمثل أسهل طريقة لأخذ البيانات من حدث دفع وإرفاقها الإشعار هو إضافة معلَمة data إلى عنصر الخيارات الذي تم تمريره في showNotification()، مثل:

const options = {
  body:
    'This notification has data attached to it that is printed ' +
    "to the console when it's clicked.",
  tag: 'data-notification',
  data: {
    time: new Date(Date.now()).toString(),
    message: 'Hello, World!',
  },
};
registration.showNotification('Notification with Data', options);

داخل معالج النقرات، يمكن الوصول إلى البيانات باستخدام event.notification.data.

const notificationData = event.notification.data;
console.log('');
console.log('The notification data has the following parameters:');
Object.keys(notificationData).forEach((key) => {
  console.log(`  ${key}: ${notificationData[key]}`);
});
console.log('');

فتح نافذة

واحدة من أكثر الردود شيوعًا على الإشعار هي فتح نافذة / علامة تبويب إلى عنوان URL محدد. يمكننا القيام بذلك باستخدام clients.openWindow() واجهة برمجة التطبيقات.

في حدث notificationclick، سنُشغِّل بعض الرموز على النحو التالي:

const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);

في القسم التالي، سنلقي نظرة على كيفية التحقق مما إذا كانت الصفحة التي نريد توجيه المستخدم إليها مفتوحة بالفعل أم لا. بهذه الطريقة، يمكننا التركيز على علامة التبويب المفتوحة بدلاً من فتح علامة التبويب .

التركيز على نافذة حالية

وعندما يكون ذلك ممكنًا، يجب أن نركز على نافذة بدلاً من فتح نافذة جديدة في كل مرة ينقر فيها المستخدم ينقر على إشعار.

قبل أن نلقي نظرة على كيفية تحقيق ذلك، تجدر الإشارة إلى لا يمكن الوصول إليها إلا للصفحات على المصدر. هذا لأنه يمكننا الاطلاع فقط على الصفحات المفتوحة التي تنتمي إلى موقعنا. وهذا يمنع من رؤية جميع المواقع التي يشاهدها المستخدمون.

بالاستناد إلى المثال السابق، سنغيّر الرمز البرمجي لمعرفة ما إذا "/demos/notification-examples/example-page.html" مفتوح حاليًا.

const urlToOpen = new URL(examplePage, self.location.origin).href;

const promiseChain = clients
  .matchAll({
    type: 'window',
    includeUncontrolled: true,
  })
  .then((windowClients) => {
    let matchingClient = null;

    for (let i = 0; i < windowClients.length; i++) {
      const windowClient = windowClients[i];
      if (windowClient.url === urlToOpen) {
        matchingClient = windowClient;
        break;
      }
    }

    if (matchingClient) {
      return matchingClient.focus();
    } else {
      return clients.openWindow(urlToOpen);
    }
  });

event.waitUntil(promiseChain);

لننتقل إلى التعليمات البرمجية.

أولاً، نحلّل صفحة المثال باستخدام واجهة برمجة تطبيقات URL. هذه خدعة أنيقة أختارها من جيف. Posnick: سيؤدي طلب الرقم new URL() باستخدام الكائن location إلى ما يلي: عرض عنوان URL مطلق إذا كانت السلسلة التي تم تمريرها نسبية (أي أن / ستصبح https://example.com/).

نجعل عنوان URL مطلقًا حتى نتمكن من مطابقته مع عنوان URL للنافذة لاحقًا.

const urlToOpen = new URL(examplePage, self.location.origin).href;

نحصل بعد ذلك على قائمة بكائنات WindowClient، وهي قائمة علامات التبويب والنوافذ المفتوحة حاليًا. (تذكَّر أنّ هذه علامات تبويب للمصدر فقط).

const promiseChain = clients.matchAll({
  type: 'window',
  includeUncontrolled: true,
});

تُعلم الخيارات التي يتم تمريرها إلى matchAll المتصفح أننا نريد فقط للبحث عن "نافذة" من النوع (أي البحث عن علامات التبويب والنوافذ) واستثناء العاملين على الويب). includeUncontrolled يسمح لنا بالبحث عن جميع علامات التبويب من المصدر التي لا تتحكم فيها الخدمة الحالية أي عامل تشغيل يشغّل هذا الرمز بشكل عام، يريدون دائمًا ضبط includeUncontrolled على "صحيح" عند طلب الرقم matchAll().

ونلتقط الوعد الذي تم إرجاعه على أنّه promiseChain حتى نتمكّن من تمريره إلى event.waitUntil() لاحقًا، مما جعل عامل الخدمة نشطًا.

عندما يتمّ حلّ المشكلة matchAll()، نكرّر الإجراءات التي نتّخذها من خلال عملاء النافذة المردودة. ومقارنة عناوين URL الخاصة بهم بعنوان URL الذي نريد فتحه. إذا وجدنا تطابقًا، نركز على العميل، مما سيلفت انتباه المستخدمين إلى تلك النافذة. يتم التركيز باستخدام مكالمة واحدة (matchingClient.focus())

إذا لم نتمكن من العثور على عميل مطابق، سنفتح نافذة جديدة، كما في القسم السابق.

.then((windowClients) => {
  let matchingClient = null;

  for (let i = 0; i < windowClients.length; i++) {
    const windowClient = windowClients[i];
    if (windowClient.url === urlToOpen) {
      matchingClient = windowClient;
      break;
    }
  }

  if (matchingClient) {
    return matchingClient.focus();
  } else {
    return clients.openWindow(urlToOpen);
  }
});

دمج الإشعارات

وقد رأينا أن إضافة علامة إلى أحد الإشعارات يؤدي إلى تفعيل سلوك سيتم استبدال الإشعار الحالي بالعلامة نفسها.

ومع ذلك، يمكنك أن تكون أكثر تعقيدًا من خلال تصغير الإشعارات باستخدام واجهة برمجة التطبيقات للإشعارات. يمكنك استخدام تطبيق دردشة، حيث قد يرغب المطوّر في تلقّي إشعار جديد عرض رسالة مشابهة للرسالة "لديك رسالتان من خالد" بدلاً من مجرد عرض أحدث .

يمكنك إجراء ذلك أو معالجة الإشعارات الحالية بطرق أخرى، وذلك باستخدام registration.getNotifications() واجهة برمجة تطبيقات تتيح لك الوصول إلى كل الإشعارات المرئية حاليًا لتطبيق الويب.

لنلقِ نظرة على الطُرق التي يمكننا من خلالها استخدام واجهة برمجة التطبيقات هذه لتنفيذ مثال المحادثة.

في تطبيق المحادثة، لنفترض أنّ كل إشعار يحتوي على بعض البيانات التي تتضمّن اسم مستخدم.

أول شيء نريد فعله هو العثور على أي إشعارات مفتوحة لمستخدم لديه حساب اسم المستخدم. سنتناول registration.getNotifications() ونراجعهما ونتحقّق من notification.data لاسم مستخدم معيّن:

const promiseChain = registration.getNotifications().then((notifications) => {
  let currentNotification;

  for (let i = 0; i < notifications.length; i++) {
    if (notifications[i].data && notifications[i].data.userName === userName) {
      currentNotification = notifications[i];
    }
  }

  return currentNotification;
});

تتمثّل الخطوة التالية في استبدال هذا الإشعار بإشعار جديد.

في تطبيق الرسائل المزيفة هذا، سنتتبع عدد الرسائل الجديدة من خلال إضافة عدد إلى بيانات الإشعار وزيادتها مع كل إشعار جديد.

.then((currentNotification) => {
  let notificationTitle;
  const options = {
    icon: userIcon,
  }

  if (currentNotification) {
    // We have an open notification, let's do something with it.
    const messageCount = currentNotification.data.newMessageCount + 1;

    options.body = `You have ${messageCount} new messages from ${userName}.`;
    options.data = {
      userName: userName,
      newMessageCount: messageCount
    };
    notificationTitle = `New Messages from ${userName}`;

    // Remember to close the old notification.
    currentNotification.close();
  } else {
    options.body = `"${userMessage}"`;
    options.data = {
      userName: userName,
      newMessageCount: 1
    };
    notificationTitle = `New Message from ${userName}`;
  }

  return registration.showNotification(
    notificationTitle,
    options
  );
});

إذا كان هناك إشعار معروض حاليًا، سنزيد عدد الرسائل ونضبط عنوان الإشعار ونص الرسالة وفقًا لذلك. إذا كان هناك لا توجد إشعارات، ننشئ إشعارًا جديدًا مع newMessageCount من 1.

والنتيجة هي أن الرسالة الأولى ستبدو كما يلي:

الإشعار الأول بدون الدمج.

سيؤدي الإشعار الثاني إلى تصغير الإشعارات إلى ما يلي:

الإشعار الثاني مع الدمج.

الأمر الجيد في هذا النهج هو أنه إذا شهد المستخدم التي تظهر واحدًا تلو الآخر، ستبدو أكثر تماسكًا من مجرد استبدال الإشعار بأحدث رسالة

الاستثناء من القاعدة

سبق أن أوضحت أنّه عليك عرض إشعار عندما تصلك رسالة فورية. صحيح معظم الوقت. أحد السيناريوهات التي لا تحتاج فيها إلى إظهار إشعار هو عندما عندما يكون المستخدم موقعك مفتوحًا ومركزًا.

داخل الحدث الفوري، يمكنك التحقّق مما إذا كنت بحاجة إلى عرض إشعار أم لا من خلال فحص عملاء النافذة والبحث عن نافذة مركزة.

يبدو رمز الحصول على جميع النوافذ والبحث عن نافذة مركّزة على النحو التالي:

function isClientFocused() {
  return clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true,
    })
    .then((windowClients) => {
      let clientIsFocused = false;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.focused) {
          clientIsFocused = true;
          break;
        }
      }

      return clientIsFocused;
    });
}

نستخدم clients.matchAll() للحصول على جميع عملاء النافذة، ثم نكرر الأمر عليهم للتحقق من المعلمة focused.

داخل حدث الدفع، سنستخدم هذه الدالة لتحديد ما إذا كنا بحاجة إلى إظهار إشعار:

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    console.log("Don't need to show a notification.");
    return;
  }

  // Client isn't focused, we need to show a notification.
  return self.registration.showNotification('Had to show a notification.');
});

event.waitUntil(promiseChain);

إرسال رسالة إلى صفحة من حدث الدفع

لقد لاحظنا أنّه يمكنك تخطّي عرض الإشعار إذا كان المستخدم يزور موقعك الإلكتروني حاليًا. لَكِنْ ماذا لو كنت لا تزال تريد إخبار المستخدم بوقوع حدث ما، ولكن الإشعار هل كانت الأيدي ثقيلة جدًا؟

أحد الأساليب هو إرسال رسالة من عامل الخدمة إلى الصفحة، بهذه الطريقة عرض إشعار أو تحديث للمستخدم لإعلامه بالحدث. هذا مفيد المواقف التي يكون فيها الإشعار الدقيق في الصفحة أفضل وأكثر ملاءمة للمستخدم.

لنفترض أننا تلقينا دفعة، وتحققنا من أن تطبيق الويب يركز في الوقت الحالي، يمكننا عندئذٍ "نشر رسالة" إلى كل صفحة مفتوحة، كما يلي:

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    windowClients.forEach((windowClient) => {
      windowClient.postMessage({
        message: 'Received a push message.',
        time: new Date().toString(),
      });
    });
  } else {
    return self.registration.showNotification('No focused windows', {
      body: 'Had to show a notification instead of messaging each page.',
    });
  }
});

event.waitUntil(promiseChain);

في كل صفحة، نستمع إلى الرسائل من خلال إضافة حدث للرسالة المستمع:

navigator.serviceWorker.addEventListener('message', function (event) {
  console.log('Received a message from service worker: ', event.data);
});

يمكنك من خلال أداة معالجة الرسائل هذه تنفيذ أي شيء تريده صفحتك أو تجاهل الرسالة تمامًا.

تجدر الإشارة أيضًا إلى أنك إذا لم تحدد مستمعًا للرسالة في صفحة الويب، لن تنفّذ الرسائل الواردة من مشغّل الخدمات أي إجراء.

تخزين صفحة في ذاكرة التخزين المؤقت وفتح نافذة

أحد السيناريوهات التي تقع خارج نطاق هذا الدليل ولكن جدير بالمناقشة هو أنه يمكنك لتحسين تجربة المستخدم بشكل عام لتطبيق الويب الخاص بك عن طريق التخزين المؤقت لصفحات الويب التي تتوقع أن يزورها المستخدمون بعد النقر على الإشعار.

يتطلّب ذلك إعداد مشغّل الخدمات للتعامل مع أحداث fetch، ولكن إذا استخدمت أداة معالجة الأحداث fetch، تأكَّد من استخدام يمكنك الاستفادة منها في حدث push عن طريق تخزين الصفحة ومواد العرض مؤقتًا التي ستحتاج إليها قبل عرض الإشعار.

توافُق المتصفح

الحدث notificationclose

دعم المتصفح

  • 50
  • 17
  • 44
  • 16

المصدر

Clients.openWindow()

دعم المتصفح

  • 40
  • 17
  • 44
  • 11.1

المصدر

ServiceWorkerRegistration.getNotifications()

دعم المتصفح

  • 40
  • 17
  • 44
  • 16

المصدر

clients.matchAll()

دعم المتصفح

  • 42
  • 17
  • 54
  • 11.1

المصدر

لمزيد من المعلومات، اطّلِع على هذه المقدمة إلى مشغّلي الخدمات. مشاركة.

الخطوات التالية

الدروس التطبيقية حول الترميز