Pola notifikasi umum

Kita akan melihat beberapa pola penerapan umum untuk web push.

Ini melibatkan penggunaan beberapa API berbeda yang tersedia di pekerja layanan.

Peristiwa penutupan notifikasi

Di bagian terakhir, kita telah melihat cara memproses peristiwa notificationclick.

Ada juga peristiwa notificationclose yang dipanggil jika pengguna menutup salah satu notifikasi Anda (yaitu, bukan mengklik notifikasi, pengguna mengklik tanda silang atau menggeser notifikasi).

Peristiwa ini biasanya digunakan untuk analisis guna melacak interaksi pengguna dengan notifikasi.

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

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

Menambahkan data ke notifikasi

Saat pesan push diterima, biasanya ada data yang hanya berguna jika pengguna telah mengklik notifikasi. Misalnya, URL yang harus dibuka saat notifikasi diklik.

Cara termudah untuk mengambil data dari peristiwa push dan melampirkannya ke notifikasi adalah dengan menambahkan parameter data ke objek opsi yang diteruskan ke showNotification(), seperti ini:

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

Di dalam pengendali klik, data dapat diakses dengan 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('');

Membuka jendela

Salah satu respons paling umum terhadap notifikasi adalah membuka jendela/tab ke URL tertentu. Kita dapat melakukannya dengan API clients.openWindow().

Dalam peristiwa notificationclick, kita akan menjalankan beberapa kode seperti ini:

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

Di bagian berikutnya, kita akan melihat cara memeriksa apakah halaman yang ingin kita arahkan kepada pengguna sudah terbuka atau belum. Dengan cara ini, kita dapat memfokuskan tab yang terbuka, bukan membuka tab baru.

Memfokuskan jendela yang ada

Jika memungkinkan, kita harus memfokuskan jendela, bukan membuka jendela baru setiap kali pengguna mengklik notifikasi.

Sebelum kami melihat cara mencapainya, perlu diperhatikan bahwa hal ini hanya dapat dilakukan untuk halaman di origin Anda. Hal ini karena kita hanya dapat melihat halaman yang terbuka yang merupakan bagian dari situs kita. Hal ini mencegah developer tidak dapat melihat semua situs yang dilihat pengguna mereka.

Dengan mengambil contoh sebelumnya, kita akan mengubah kode untuk melihat apakah /demos/notification-examples/example-page.html sudah terbuka.

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

Mari kita pelajari kode tersebut.

Pertama, kita mengurai halaman contoh menggunakan URL API. Ini adalah trik keren yang saya pelajari dari Jeff Posnick. Memanggil new URL() dengan objek location akan menampilkan URL absolut jika string yang diteruskan bersifat relatif (yaitu / akan menjadi https://example.com/).

Kita membuat URL menjadi absolut sehingga kita dapat mencocokkannya dengan URL jendela nanti.

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

Kemudian, kita mendapatkan daftar objek WindowClient, yang merupakan daftar tab dan jendela yang saat ini terbuka. (Ingat, tab ini hanya untuk origin Anda.)

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

Opsi yang diteruskan ke matchAll akan memberi tahu browser bahwa kita hanya ingin menelusuri klien jenis "window" (yaitu cukup mencari tab dan jendela, serta mengecualikan pekerja web). includeUncontrolled memungkinkan kita menelusuri semua tab dari origin Anda yang tidak dikontrol oleh pekerja layanan saat ini, yaitu pekerja layanan yang menjalankan kode ini. Secara umum, Anda akan selalu ingin includeUncontrolled bernilai benar saat memanggil matchAll().

Kita mengambil promise yang ditampilkan sebagai promiseChain sehingga kita dapat meneruskannya ke event.waitUntil() nanti, sehingga service worker tetap aktif.

Saat promise matchAll() di-resolve, kita akan melakukan iterasi melalui klien jendela yang ditampilkan dan membandingkan URL-nya dengan URL yang ingin kita buka. Jika kami menemukan kecocokan, kami memfokuskan klien tersebut, yang akan membawa jendela tersebut ke perhatian pengguna. Pemfokusan selesai dengan panggilan matchingClient.focus().

Jika tidak dapat menemukan klien yang cocok, kita akan membuka jendela baru, sama seperti di bagian sebelumnya.

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

Menggabungkan notifikasi

Kami melihat bahwa menambahkan tag ke notifikasi akan memilih perilaku saat notifikasi yang ada dengan tag yang sama akan diganti.

Namun, Anda dapat melakukan tindakan yang lebih canggih dengan menciutkan notifikasi menggunakan Notifications API. Pertimbangkan aplikasi chat, tempat developer mungkin ingin notifikasi baru menampilkan pesan yang mirip dengan "Anda memiliki dua pesan dari Matt", bukan hanya menampilkan pesan terbaru.

Anda dapat melakukannya, atau memanipulasi notifikasi saat ini dengan cara lain, menggunakan API registration.getNotifications() yang memberi Anda akses ke semua notifikasi yang saat ini terlihat untuk aplikasi web Anda.

Mari kita lihat bagaimana kita dapat menggunakan API ini untuk menerapkan contoh chat.

Di aplikasi chat, misalkan setiap notifikasi memiliki beberapa data yang mencakup nama pengguna.

Hal pertama yang ingin kita lakukan adalah menemukan notifikasi terbuka untuk pengguna dengan nama pengguna tertentu. Kita akan mendapatkan registration.getNotifications() dan melakukan loop di atasnya serta memeriksa notification.data untuk nama pengguna tertentu:

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

Langkah selanjutnya adalah mengganti notifikasi ini dengan notifikasi baru.

Di aplikasi pesan palsu ini, kita akan melacak jumlah pesan baru dengan menambahkan jumlah ke data notifikasi baru dan menambahkannya dengan setiap notifikasi baru.

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

Jika ada notifikasi yang saat ini ditampilkan, kita akan menambah jumlah pesan dan menetapkan judul serta pesan isi notifikasi. Jika tidak ada notifikasi, kita akan membuat notifikasi baru dengan newMessageCount 1.

Hasilnya adalah pesan pertama akan terlihat seperti ini:

Notifikasi pertama tanpa penggabungan.

Notifikasi kedua akan menciutkan notifikasi menjadi seperti ini:

Notifikasi kedua dengan penggabungan.

Keuntungan pendekatan ini adalah jika pengguna melihat notifikasi muncul di atas yang lain, notifikasi akan terlihat dan terasa lebih kohesif daripada hanya mengganti notifikasi dengan pesan terbaru.

Pengecualian untuk aturan

Kami telah menyatakan bahwa Anda harus menampilkan notifikasi saat menerima push dan hal ini umumnya benar. Satu skenario saat Anda tidak perlu menampilkan notifikasi adalah saat pengguna membuka dan memfokuskan situs Anda.

Di dalam peristiwa push, Anda dapat memeriksa apakah Anda perlu menampilkan notifikasi atau tidak dengan memeriksa klien jendela dan mencari jendela yang difokuskan.

Kode untuk mendapatkan semua jendela dan mencari jendela yang difokuskan akan terlihat seperti ini:

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

Kita menggunakan clients.matchAll() untuk mendapatkan semua klien jendela, lalu melakukan loop untuk memeriksa parameter focused.

Di dalam peristiwa push, kita akan menggunakan fungsi ini untuk memutuskan apakah perlu menampilkan notifikasi:

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

Mengirim pesan ke halaman dari peristiwa push

Kami telah melihat bahwa Anda dapat melewati tampilan notifikasi jika pengguna saat ini berada di situs Anda. Namun, bagaimana jika Anda tetap ingin memberi tahu pengguna bahwa suatu peristiwa telah terjadi, tetapi notifikasi terlalu berat?

Salah satu pendekatannya adalah dengan mengirim pesan dari pekerja layanan ke halaman. Dengan cara ini, halaman web dapat menampilkan notifikasi atau update kepada pengguna, yang memberi tahu mereka tentang peristiwa tersebut. Hal ini berguna untuk situasi saat notifikasi halus di halaman lebih baik dan lebih mudah bagi pengguna.

Misalnya, kita telah menerima push, memeriksa bahwa aplikasi web kita saat ini difokuskan, lalu kita dapat "memposting pesan" ke setiap halaman yang terbuka, seperti ini:

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

Di setiap halaman, kita memproses pesan dengan menambahkan pemroses peristiwa pesan:

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

Di pemroses pesan ini, Anda dapat melakukan apa pun yang Anda inginkan, menampilkan UI kustom di halaman atau mengabaikan pesan sepenuhnya.

Perlu juga diperhatikan bahwa jika Anda tidak menentukan pemroses pesan di halaman web, pesan dari pekerja layanan tidak akan melakukan apa pun.

Menyimpan halaman ke dalam cache dan membuka jendela

Satu skenario yang berada di luar cakupan panduan ini, tetapi perlu dibahas adalah Anda dapat meningkatkan UX aplikasi web secara keseluruhan dengan meng-cache halaman web yang diharapkan akan dikunjungi pengguna setelah mengklik notifikasi Anda.

Hal ini memerlukan penyiapan pekerja layanan untuk menangani peristiwa fetch, tetapi jika Anda menerapkan pemroses peristiwa fetch, pastikan Anda memanfaatkannya dalam peristiwa push dengan meng-cache halaman dan aset yang diperlukan sebelum menampilkan notifikasi.

Kompatibilitas browser

Peristiwa notificationclose

Dukungan Browser

  • Chrome: 50.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Sumber

Clients.openWindow()

Dukungan Browser

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Sumber

ServiceWorkerRegistration.getNotifications()

Dukungan Browser

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Sumber

clients.matchAll()

Dukungan Browser

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 54.
  • Safari: 11.1.

Sumber

Untuk informasi selengkapnya, lihat postingan pengantar service worker ini.

Langkah berikutnya

Codelab