ใช้งานการ Push Notification ด้วย Service Worker

บทความ เมื่อไม่กี่สัปดาห์ โดย Ninenik Narkdee
push notification push api service worker

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ push notification push api service worker



เนื้อหาตอนที่แล้วเราได้รู้จักวิธีการใช้งาน Notifications API ที่
สามารถนำไปประยุกต์เบื้องต้นไปแล้ว ซึ่งความสามารถก็ถือว่า
เพียงพอกับการใช้งานทั่วไป และมีวิธีการจัดการไม่ยุ่งยากมากนัก
สามารถทบทวนเนื้อหาตอนที่แล้วได้ที
 
    การใช้งาน Notifications API แจ้งเตือนผ่านเว็บบราวเซอร์ http://niik.in/1030 
 
เนื้อหาในบทความนี้เป็นการต่อยอดความสามารถจาก Notification เดิม
โดยความแตกต่างที่เห็นได้ชัดก็คือ สามารถรับการแจ้งเตือนแม้จะปิดหน้าเพจนั้นไปแล้ว
หรือไม่ต้องเปิดหน้าที่รันคำสั่งแจ้งเตือนทิ้งไว้เหมือนรูปแบบแรก ซึ่งเข้าใจง่ายๆ ก็
เหมือนกับการแจ้งเตือนผ่านแอพในมือถือทั่วไป
    เมื่อความสามารถมากขึ้น วิธีการและรูปแบบการใช้งานก็จะมีความซับซ้อนขึ้นมาในระดับหนึ่ง
และมีส่วนที่ต้องกำหนดและใช้งานเพิ่มเติม เช่น การใช้งาน service worker ซึ่งเป็นตัวเชื่อมการ
ทำงานระหว่างเว็บเพจหรือเว็บแอพของเรา กับ บราวเซอร์ และ เครือข่าย
    อย่างไรก็ตาม ก่อนที่จะลงไปในรายละเอียดมากกว่านี้ ต้องขอบอกก่อนว่า ไม่แนะนำให้ใช้งาน Push
Notification สำหรับกลุ่มผู้ใช้งานทั่วไป เช่นว่า จะใช้เพื่อแจ้งข่าวสาร อัพเดท หรือทำโปรโมชั่น ต่างๆ
เหล่านี้ เท่าที่ทดสอบดู ผู้เขียนมองว่า การใช้งาน push ไม่ค่อยมีความสเถียรมากนัก และบราวเซอร์เอง
ก็มีการเปลี่ยนแปลงการอนุญาตใช้งาน รวมถึงการคำนึงถึงการส่งข้อมูลไปหากลุ่มผู้ใช้พร้อมกันหลายๆ คน
และความยุ่งยากในการจัดการ เหล่่านี้เป็นต้น
    จากที่ทดสอบดูจะสามารถใช้งานกับบราวเซอร์ chrome และ edge ได้ในระดับหนึ่ง แต่ก็จะไม่สามารถ
ส่งข้อมูลกลับมาหาผู้ใช้งานได้โดยตรง เพราะความสำคัญของความปลอดภัยของข้อมูล ซึ่งจะต้องเข้ารหัส
ทางผู้เขียนยังไม่พบวิธีในการแก้ปัญหา แต่ก็มีแนวทางเบื้องต้น ที่ทดแทนได้แนะนำไว้
    ดังนั้นการใช้งาน Push Notification กรณีนี้จึงน่าจะเหมาะกับพัฒนามาเพื่อใช้งานในกลุ่มเล็กๆ หรือเพื่อ
ใช้งานเอง เช่น แจ้งเตือนมีข้อความใหม่ในเว็บไซต์ของตัวเอง มีรายการสั่งซื้อใหม่เข้ามา หรืออื่นๆ ที่ไม่ใช้การ
นำไปใช้กับกลุ่มผู้ใช้จำนวนมาก อย่างไรก็ตาม หากจะนำไปประยุกต์ใช้กับการใช้งานทั่วไป ก็ยังทำได้ แต่ก็จะ
มีข้อจำกัดในบางบราวเซอร์ ก็เลือกพิจารณากันดู
 

 

เข้าใจการทำงานระบบ Push Notification อย่างง่าย

    จะขออธิบายการทำงานอย่างง่าย อาจจะไม่ครบถ้วนทีเดียว คือ เมื่อผู้ใช้เปิดมายังหน้าเพจ
หรือหน้าเว็บไซต์ของเรา ก็จะทำการ subscription เพื่อรับการแจ้ง push ซึ่งการที่จะสามารถ subscription
ได้หรือไม่ก็ขึ้นกับการกำหนดการอนุญาตการแจ้งเตือน ( Notification ) ด้วยว่าเปิดใช้งานและอนุญาตไหม
หากเปิดใช้งานและอนุญาต ตัว service worker ก็จะให้ค่า Subscription object มา โดยจะมีค่า property
ที่ชื่อ endpoint เป็น url ที่จะบอกว่า ค่านี้เป็นไอดีของเครื่องเรา  เพื่อที่เวลาจะใช้ส่งข้อความ push
กลับมาให้ได้ถูกต้อง ค่า endpoint จะต้องถูกเก็บบันทึกไว้ ที่ฝั่ง server เช่นอาจจะเก็บไว้ในฐานข้อมูลก็ได้
ฝั่งผู้ใช้ก็เสร็จเท่านี้ ที่เหลือก็เป็นฝั่งผู้ให้บริการ หรือคนที่จะส่งการแจ้งเตือน ก็จะไปดึงค่า endpoint ของผู้ใช้ทั้ง
หมดที่ส่งมาเก็บไว้ แล้วนำมาส่งข้อความ push ด้วยคำสั่งที่กำหนด  เมื่อส่งข้อความ push ฝั่งผู้ใช้ถึงแม้จะปิด
หน้าเพจไปแล้วแต่ตัวโปรแกรมยังเปิดอยู่ หมายถึงบราวเซอร์ยังใช้งานเว็บไซต์อื่นอยู่ หรือกรณีมือถือ ก็อาจจะทำงานอยู่
เบื่องหลังอยู่ ก็จะยังมีการแจ้งเตือนขึ้น เมื่อได้รับข้อความ push ซึ่งทำงานโดยตัว service worker อยู่เบื้องหลัง 
คอยรับการแจ้งเตือน รูปแบบการทำงานคร่าวๆ ก็ประมาณนี้
 
 

สิ่งที่ต้องมีเมื่อจะใช้งาน Push Notification

    ก่อนที่จะลงไปในรายละเอียดวิธีการ จะบอกถึงข้อกำหนด ในการใช้งาน และส่วนต่างๆ ที่จะต้องนำมา
ใช้งานร่วม โดยในที่นี้จะใช้ฝั่ง server เป็น php  สิ่งที่ต้องเตรียมมีดังนี้
    - ต้องใช้งานผ่าน https
    - มีการใช้งาน Firebase cloud messaging สมัครและเอาแค่ค่า Sender ID มาใช้
    - มีการใช้งาน Web Push library for PHP สำหรับส่งข้อมูล และสร้าง key
    - มีการใช้งาน service worker คอยรับการ push
    - ใช้งาน Notification API สำหรับแสดงการแจ้งเตือน
 
 

สร้าง Public Key และ Private Key ด้วย Web Push library for PHP

    ให้เราทำการติดตั้ง Web Push library for PHP
 
    สามารถทำที่ localhost โดยใช้ server จำลองอย่าง xampp สมมติเราจะใช้งานการแจ้งเตือน
ในโฟลเดอร์ชื่อ webpush ก็จะตั้ง ผ่าน command line ได้ดังนี้
 
composer require minishlink/web-push


 
 




 
    ก็จะได้ข้อมูล Web Push library ในโฟลเดอร์ vendor มาใช้งาน

 
    


 
 
    ให้เราสร้างไฟล์ชื่อ push.php และกำหนดคำสั่งดังนี้
 
<?php
// include composer autoload
require_once './vendor/autoload.php';  // กำหนด path ให้ถูกต้อง

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\VAPID;

echo "<pre>";
$keys = VAPID::createVapidKeys();
print_r($keys);
?>
 
    ทำการเรียกใช้งาน จะได้ค่า 

 
 


 
 
    ให้เอาค่าทั้งสอง publicKey และ privateKey บันทึกไว้ในไฟล์ txt ชื่อ
    private_key.txt และ public_key.txt ไว้ในโฟลเดอร์ keys คล้ายรูปด้านล่าง

 
 


 
 
    โครงสร้างในโฟลเดอร์ webpush ก็จะเป็นปะมาณนี้

 
 


 
 
    ค่า publickey และ privatekey เราจะสร้างมาแค่ครั้งเดียว 
    การเปลี่ยนแปลง หรือใช้ค่าใหม่ จะมีผลกับรายการเดิม ที่เคยใช้งานมาแล้ว ซึ่งถ้าเปลี่ยนค่าใหม่
ก็ต้องส่งค่าจากผู้ใช้มาเก็บบันทึกใหม่อีกครั้ง ดังนั้นพยายามหลีกเลี่ยงการเปลี่ยนค่าใหม่ ถ้ามีจำนวน
การใช้งานมากแล้ว
 
    ค่า publickey จะถูกใช้งานทั้งในฝั่ง server ผ่านคำส่ง php และ ฝั่ง client ผ่านคำสั่ง javascript
 
    ตอนนี้เราจัดการฝั่ง server เบื้องต้นเสร็จแล้ว และจะกลับมาดูต่อในหัวข้ออื่นอีกครั้ง 
 

 

การสมัครใช้งาน Firebase cloud messaging 

    Firebase cloud messaging จะเป็นเสมือนตัวที่ทำหน้าที่ส่งข้อความ push จากชุดคำสังจากเราอีกที
ค่าที่เราต้องการจากการใช้งาน Firebase cloud messaging ตัวเลข Sender ID เท่านั้น ดังนั้น ให้เราสมัครใช้งาน
และนำค่า Sender ID มาไว้ใช้ ให้ดูวิธีการจากบทความด้านล่าง ดูเฉพาะส่วนของการสมัครใช้งานก็พอ

 
 


 
 
    เตรียมพร้อม ใช้งาน Push Notification ใน ionic material app http://niik.in/748


 
 

การใช้งาน service worker คอยรับการ push

    ต่อไปเป็นส่วนของการใช้งาน javascript จริงๆ แล้วตัว service worker สามารถทำหลายๆ อย่างได้ เช่น
การกำหนดให้สามารถเพิ่ม home screen ในหน้าจอหลักได้ สามารถดูแนวทางได้ที่ คำถามในกระดานถามตอบ
ที่ลิ้งค์ http://niik.in/que_3154_7011
    แต่ในที่นี้จะใช้สำหรับรับข้อความ push เริ่มต้นดังนี้ ให้เราสร้างไฟล์ manifest.json ไว้ที่ root ของเว็บไซต์
ของเรา แล้วเรียกใช้งานในหน้าที่มีการใช้งานหลัก โดยแทรกไว้ในส่วนของ <head> ดังนี้
 
<head>
.....
  <link rel="manifest" href="/manifest.json">   
.....
 
ไฟล์ manifest.json
 
{
    "name": "Ninenik.com",
    "gcm_sender_id": "ใส่ตัวเลข send id ของเรา" 
}
 
    หรือสมมติเราจะรองรับให้กำหนด home screen ได้ ก็เพิ่มไปแบบนี้ก็ได้
 
{
	"name": "Ninenik.com",
	"short_name": "Ninenik",
	"start_url": "/?app",
	"display": "standalone",
	"background_color": "#f08b86",
	"Theme_color": "#D60000",								
	"icons": [
		{
		"src": "/images/logo-2-512x512.png",
		"sizes": "512x512",
		"type": "image/png",
		"density": "4.0"
		}
	],	
	"gcm_sender_id": "ใส่ตัวเลข send id ของเรา"
}
 
    ต่อไปให้สร้าง service-worker.js โดยต้องไว้ที่ root ของเว็บไซต์เหมือนกัน
 

ไฟล์ service-worker.js

 
// ทำงานเมื่อ fetch ในที่นี้ไม่ได้ใช้
self.addEventListener('fetch', function(event){
 //   console.log(event);
});
// ทำงานเมื่อมีการปิดการแจ้งเตือน จากปุ่มปิดเท่านั้น
self.onnotificationclose = function(e){
   console.log("close");
      console.log(e);
}
// ทำงานเมื่อมีการ push ข้อความเข้ามา
self.addEventListener('push', function(e) {
 //   console.log(e);
});
    
 
    จากนั้นในไฟล์ที่ใช้งานหลัก หรือไฟล์ที่มีการใช้งาน manifest.json ก็ให้เพิ่มคำสั่งการเรียกใช้งาน
sevice worker ลงไปดังนี้ ด้านล่างสุดก่อนปิดแท็ก </body>
    สมมติไว้ในไฟล์หลัก index.html
 
<!--กำหนดการใช้งาน service worker-->
<script type="text/javascript">  
if ('serviceWorker' in navigator) {
    console.log("Will the service worker register?");
    navigator.serviceWorker.register('service-worker.js')
    .then(function(reg){
         console.log("Yes, it did.");
    }).catch(function(err) {
        console.log("No it didn't. This happened: ", err)
    });
}
</script>  
 
    ทดสอบเรียกใช้งาน ถ้าไม่มีอะไรผิดพลาด ก็จะขึ้นข้อความในแท็บ console ว่า Yes, it did.

 
 


 
 
    และในแท็บ Application เราก็จะเห็นว่าตัว service worker ทำงานอยู่

 
 


 
 
    แท็บส่วนนี้มีความสำคัญกรณีทดสอบ เพราะเวลาแก้ไขไฟล์ service-worker.js จะไม่มีผลใดๆ 
นอกจากปิดบราวเซอร์ทั้งหมดแล้วเปิดใหม่ แต่ถ้าเราจัดการหน้าแท็บ Application นี้ หลังจากแก้ไข
ไฟล์ service-worker.js ทุกครั้ง เราสามารถมากดปุ่ม stop หลัง status แล้วกดปุ่ม start ใหม่ได้
ทำให้ไม่ต้องคอยปิดบราวเซอร์ใหม่ทุกครั้ง
    จำไว้ว่าการแก้ไขไฟล์ service-worker.js จะมีผลกับฝั่งผู้ใช้งานด้วย เพราะถ้าฝั่งผู้ใช้เคยโหลด
หน้าเว็บเรามาก่อนแล้ว ยังไม่เคยเปิดโปรแกรม หรือล้างค่า cache ค่าก็จะเป็นของไฟล์  service-worker.js 
ตัวเก่า ดังนั้นพยายามหลักเลี่ยงการแก้ไขหลังจากใช้งาน หรือให้ทำการลบแคสหรือปิดและเปิดโปรแกรม
ใหม่แทนก็ได้


 
 

การทำคำสั่งสมัครรับการแจ้งเตือน Subscription Push Notification

    ถ้าเราได้ทำการตรวจสอบและติดตั้งการใช้งาน service worker เรียบร้อยแล้ว ก็มาถึงส่วนของการส่ง
ค่าเพื่อให้ผู้ใช้สมัครรับการแจ้งเตือน แนวทางก็คือ เราจะตรวจสอบว่ามีการรับการแจ้งเตือนแล้วหรือไม่
ถ้ายังก็ให้ทำการส่งคำร้องขอรับการแจ้งเตือน และถ้าสำเร็จก็จะได้ค่า sub.endpoint ที่เราจะบันทึก
ลงฐานข้อมูล โดยอาจจะใช้เป็น ajax ก็ได้ ในที่นี้จะไม่ขอพูดถึง  หรือถ้าต้องการทดสอบ เราก็ให้ copy
ค่า endpoint ไว้ใช้ทดสอบแทนการบันทึกก่อนก็ได้ 
    ในการส่งค่าเพื่อรับการแจ้งเตือน จะมีการกำหนดค่า publickey ค่านี้เราจะได้จากวิธีด้านบน ให้นำค่านั้น
มาแทนลงไปในโค้ดตัวอย่าง
    ให้เราแก้ไขในส่วนของการใช้งาน service worker เพิ่มเติมเป็นดังนี้

<!--กำหนดการใช้งาน service worker-->
<script type="text/javascript">  
if ('serviceWorker' in navigator) {
  //  console.log("Will the service worker register?");
    navigator.serviceWorker.register('service-worker.js')
    .then(function(reg){
   //     console.log("Yes, it did.");

            // ตรวจสอบสถานะการรับการแจ้งเตือน
            reg.pushManager.getSubscription().then(function(sub) {
                if (sub === null) { // ยังไม่รับการแจ้งเตือน
                    // ยังไม่รับการแจ้งเตือน เราอาจจะสร้างปุ่มให้กด รับการแจ้งเตือก็ได้
                    console.log('Not subscribed to push service!');
                    // เรียกใช้การทำคำสังรับการแจ้งเตือน
                    // ในที่นี้เราจะเรียกคำสั่งรับการแจ้งให้เลย
                    subscribeUser();
                } else {
                    // ทำการรับการแจ้งเตือนแล้ว อาจจะเอาค่า sub.endpoint
                    // ไปเช็คอีกทีในฐานข้อมูลก็ได้่ว่าได้บันทึกแล้วหรือยัง ถ้ายังก็บันทึกลงไป
                    console.log('Subscription object: ', sub);
                    console.log('Endpoint URL: ', sub.endpoint);
                }
            });

            // ฟังก์ชั่นจัดรูปแบบข้อมูล public key ก่อนส่งค่าไป
            function urlBase64ToUint8Array(base64String) {
                const padding = '='.repeat((4 - base64String.length % 4) % 4);
                const base64 = (base64String + padding)
                .replace(/\-/g, '+')
                .replace(/_/g, '/');

                const rawData = window.atob(base64);
                const outputArray = new Uint8Array(rawData.length);

                for (var i = 0; i < rawData.length; ++i) {
                outputArray[i] = rawData.charCodeAt(i);
                }
                return outputArray;
            }     

            // ฟังก์ชั่นสำหรับรับการแจ้งเตือน push
            function subscribeUser(){
                // กำหนดค่า public key ที่ได้จากขั้นตอนการส้ราง publickey
                var publicKey = urlBase64ToUint8Array("ค่า public key");
                // ทำคำสั่งรับการแจ้งเตือน
                reg.pushManager.subscribe({
                    userVisibleOnly: true,
                    applicationServerKey: publicKey
                }).then(function(sub) { // ทำคำสั่งรับการแจ้งเตือนสำเร็จ
                    // เอาค่านี้ sub.endpoint ไปบันทึกลงฐานข้อมูล
                    console.log('Endpoint URL: ', sub.endpoint);
                }).catch(function(e) {
                    if (Notification.permission === 'denied') { //  ไม่ได้เปิดหรืออนุญาต Notification
                        console.warn('Permission for notifications was denied');
                    } else { // รับการแจ้งเตือนไม่ได้อื่นๆ 
                        console.error('Unable to subscribe to push', e);
                    }
                });              
            }

    }).catch(function(err) {
        console.log("No it didn't. This happened: ", err)
    });
}
</script>
 
 
 

ทำการรับค่า Push และทำการแจ้งเตือน

    ต่อไปเป็นส่วนสุดท้ายของฝั่งผู้ใช้ ก็คือการกำหนดการรับค่า push ให้กับ service worker และทำคำสั่ง
แจ้งเตือนผ่าน Notification API ให้เราไปปรับแก้ไขไฟล์ service-worker.js เพิ่มเติม ดังนี้
    หมายเหตุ: การแก้ไขไฟล์แล้ว หากจะใช้งานต้องไปปิด และเปิด service ใหม่ ในหน้าแท็บ Applicaton
ตามที่ได้อธิบายไว้ด้านบน
 
    สมมติเราจะแค่ทดสอบก่อนว่า ถ้ามีการส่งข้อความ push ส่วนนี้จะทำงานหรือไม่ โดยเพิ่มส่วนของการ
รับการ push ดังนี้เข้าไป รูปแบบการแจ้งเตือนจะแค่กำหนดค่าตายตัวไปก่อน ข้อสังเกตคือใน Notification
API ที่ใช้กับ service worker เราสามารถกำหนด action เพิ่มเข้ามาได้ 
    คำสั่งข้างล่าง เมื่อมีการ push ข้อความใดๆ มาก็ตาม การแจ้งเตือนก็จะแสดงตามรูปแบบที่กำหนดเท่านั้น
 
// ทำงานเมื่อมีการ push ข้อความเข้ามา
self.addEventListener('push', function(e) {
//   console.log(e);
   // แจ้งเตือนทุกเครื่องที่ รับการแจ้ง
   clients.matchAll().then(function(c) {
      self.registration.showNotification('หัวข้อการแจ้งเตือน!', {
            icon: 'http://www.ninenik.com/images/logo_01_Tue.gif',
            body: 'รายละเอียดการแจ้ง!',
            image:'https://www.ninenik.com/images/ninenik_page_logo.png',
            requireInteraction:true,
            actions: [
                  {
                     action: 'explore', 
                     title: 'Check it',
                     icon: 'https://www.ninenik.com/images/ninenik_page_logo.png'
                  },
                  {
                     action: 'close', 
                     title: 'Close',
                     icon: 'https://www.ninenik.com/images/ninenik_page_logo.png'
                  },
            ]            
         }); 
   });
});
 
    ผลลัพธิ์ที่ได้ 

 
 


 
 
    การแจ้งเตือนใน service worker รองรับการตั้งค่าเหมือนการแจ้งเตือนในบทความตอนที่แล้ว แต่ก็จะ
มีความสามารถเพิ่มเข้ามา เช่น มีส่วนของการกำหนด action เพิ่มเข้าไปในการแจ้งเตือนได้ ตามรูป
 
    ปัญหาหนึ่งที่พบในปัจจุบันและยังหาวิธีการแก้ไขไม่ได้คือ เราไม่สามารถส่งข้อความใดๆ กลับมายังผู้ใช้ได้
อย่างที่ได้เกริ่นไปในตอนแรก ถ้าใครมีวิธีก็สามารถประยุกต์ปรับใช้งานได้ 
    สิ่งที่ทำได้คือเมื่อมีการรันคำสั่น push ข้อความฝั่ง server ก็จะมีการแจ้งหรือเกิด push event ขึ้นใน 
service worker และการแจ้งเตือนแสดงขึ้น ถ้าเราต้องการให้มีข้อความตามที่กำหนด วิธีการแก้ปัญหา
ในขณะนี้คือ การกำหนดให้ fetch ข้อมูล หรือ ajax ดึงข้อมูลที่ต้องการมาใช้แล้วนำมาแสดงในการแจ้งเตือน
อีกที วิธีการคร่าวจะประมาณนี้ สมมติเช่น เราจะทำการดึงข้อมูลจาก ข้อมูลทดสอบมาแสดง ก็สามารถทำได้ดังนี้
 
// ทำงานเมื่อมีการ push ข้อความเข้ามา
self.addEventListener('push', function(e) {
   console.log(e);
   // แจ้งเตือนทุกเครื่องที่ รับการแจ้ง
   clients.matchAll().then(function(c) {
      // ไปดึงข้อมูลจากฐานข้อมูล หรือไฟล์ที่กำหนด
      fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(function(data){
      //  console.log(data);
         // แล้วนำค่ามาใช้งานในการแจ้งเตือน ตัวอย่างนี้ใช้แค่ title กับ body
         self.registration.showNotification(data[0].title, { 
            icon: 'http://www.ninenik.com/images/logo_01_Tue.gif', 
            body: data[0].body,
            image:'https://www.ninenik.com/images/ninenik_page_logo.png',
            requireInteraction:true, 
            actions: [
                  {
                     action: 'explore', 
                     title: 'Check it',
                     icon: 'https://www.ninenik.com/images/ninenik_page_logo.png'
                  },
                  {
                     action: 'close', 
                     title: 'Close',
                     icon: 'https://www.ninenik.com/images/ninenik_page_logo.png'
                  },
            ]            
         }); 

      });
   });
});
 
    ผลลัพธ์ที่ได้

 
 


 
 
    ถ้าเราอยากเปลี่ยนแปลงข้อมูลอะไร หรืออยากให้ข้อมูลอะไรส่งมาแจ้งกับทางผู้ใช้ก็สามารถกำหนดได้ในข้อมูลที่
เรียกใช้งานผ่านการ fetch หรือใช้ ajax ก็ได้
 
    ในโค้ดข้างต้น มีส่วนหนึ่งที่เราสามารถประยุกต์เพิ่มเติม คือการตรวจสอบเครื่องที่กำลังใช้งาน อยู่
 
// แจ้งเตือนทุกเครื่องที่ รับการแจ้ง 
clients.matchAll().then(function(c) {

});
 
    เราสามารถกรอง หรือจัดการเพิ่มเติมได้ดังนี้ 
 
// แจ้งเตือนทุกเครื่องที่ รับการแจ้ง    
clients.matchAll().then(function(clis) {
   // ดูสถานะของแต่ละเครื่อง
   var client = clis.find(function(c) {
      c.visibilityState === 'visible';
   });   
   // มีเปิดหน้าบราวเซอร์เว็บเพจอยู่ จะเป็นเว็บอื่นก็ได้
   if (client !== undefined) { 
         // ทำคำสั่งตามต้องการ
   } else { // ไม่มีการเปิดหน้าต่างเว็บเพจใดๆ  
         // ทำคำสั่งตามต้องการ
   }
});   
 
    กลับมาส่วนการทำงานการแจ้งเตือน เมื่อ notification หรือการแจ้งเตือนแสดงขึ้น เราสามารถ
กำหนด event ให้กับการแจ้งเตือนได้ คล้ายๆ กับการใช้งานในบทความตอนที่แล้ว แต่ใน service worker
จะทำได้ 2 event คือ เมื่อ click และ close ด้วยรูปแบบคำสั่งดังนี้
 
// ทำงานเมื่อมีการปิดการแจ้งเตือน จากปุ่มปิดเท่านั้น
self.onnotificationclose = function(e){
   console.log("close");
      console.log(e);
}
// ทำงานเมื่อมีการคลิก รวมถึงปุ่ม action ถ้ามี ไม่รวมปุ่มปิด
self.onnotificationclick = function(e) {
   console.log("click");
   console.log(e);
}
 
    ปกติแล้วการแจ้งเตือน ถ้า requireInteraction เป็น true ก็จะมีปุ่ม close หรือปุ่มปิดเข้ามาด้วย และสามารถใช้งาน
กับ event onclose เมื่อกดปุ่มนี้ได้ แต่ถ้ามีการใช้งาน action หรือกำหนดปุ่มเพิ่มเติม ก็จะไม่มีปุ่ม close ขึ้น การกดปุ่ม
action จะเข้าเงื่อนการทำงานของ event onclick รวมถึงการกดที่ตัวแจ้งเตือนด้วย 
    การกดที่ปุ่ม action สามารถเลือกกำหนดการทำงาน ตามค่า action ที่กดได้ ในรูปแบบดังนี้
 
self.onnotificationclick = function(e) {
   console.log("click");
   if(e.action == "explore"){
      console.log("explore action");
   }
   if(e.action == "close"){
      console.log("close action");
   }
   console.log(e);
}
 
    ก็สามารถเลือกปรับใช้งานว่าปุ่มไหนให้ทำคำสั่งใดๆ ตามต้องการ
    การกำหนดการทำงานฝั่งผู้ใช้เบื้องต้นก็มีเพียงเท่านี้ ต่อไปจะเป็นส่วนของฝั่งผู้ส่งข้อความ push หรือส่ง
การแจ้งเตือน ก็จะมีรูปแบบคำสั่งไม่ยุ่งยาก ดังนี้
 

 

การส่งการแจ้งเตือนหรือ Push Notification

    กลับมาในฝ่่ง server เราจะทดสอบการส่งการแจ้งเตือน ในที่นี้จริงๆ ต้องการส่งข้อมูลไปด้วย แต่จากที่ทดสอบดู
เหมือนจะสามารถส่งการแจ้งเตือน หรือสร้าง push event ให้กับ service worker เท่านั้น ตามที่ได้อธิบายไปแล้ว
ในบทความ ฝั่ง server ให้เรามาใช้ไฟล์เดิมก็ได้คือ push.php แล้วกำหนดโค้ดเบื้องต้นอย่างง่ายดังนี้
 

ไฟล์  push.php

 
<?php
// include composer autoload
require_once './vendor/autoload.php';  // กำหนด path ให้ถูกต้อง

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\VAPID;

$subscription = Subscription::create([
    "endpoint" => "https://fcm.googleapis.com/fcm/send/diwFCFhoNes:APA91n5so-Ej6oOs2zjTW6Wzdq",
    "contentEncoding" => "aesgcm",
]);

$auth = array(
    'VAPID' => array(
        'subject' => 'https://www.ninenik.com',
        'publicKey' => file_get_contents('./keys/public_key.txt'), // don't forget that your public key also lives in app.js
        'privateKey' => file_get_contents('./keys/private_key.txt'), // in the real world, this would be in a secret file
    ),
);

$webPush = new WebPush($auth);

$payload = [
	"message" => "This is test"
];
$report = $webPush->sendOneNotification(
    $subscription,
	json_encode($payload)
);

// handle eventual errors here, and remove the subscription from your server if it is expired
$endpoint = $report->getRequest()->getUri()->__toString();

if ($report->isSuccess()) {
    echo "[v] Message sent successfully for subscription {$endpoint}.";
} else {
    echo "[x] Message failed to sent for subscription {$endpoint}: {$report->getReason()}";
}
?>
 
    เวลาทดสอบให้เราใส่ค่า endpoint ของเราสำหรับทดสอบลงไป จากนั้นลองทดสอบรันผ่านบราวเซอร์ดูผลลัพธ์

 
 



 
 
    ก็จะเกิด push event สำหรับ service worker ขึ้น และการแจ้งเตือนก็แสดงขึ้นปกติ

 
 


 
 
    สังเกตว่าส่วนของข้อมูลใน log ของ service worker ตรง data เป็น null ไม่มีการส่งข้อมูลใดๆ มา
    ต่อไปสมมติเราจะส่งไปหาผู้ใช้ 2 ราย ที่ดึงข้อมูลจากฐานข้อมูลมาเป็น array สามารถทำได้ดังนี้
 
<?php
// include composer autoload
require_once './vendor/autoload.php';  // กำหนด path ให้ถูกต้อง

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\VAPID;

$arr_endpoint = [
	"https://sg2p.notify.windows.com/w/?tVe3kMbiU9QZNHhaEH4h5g%3d",
	"https://fcm.googleapis.com/fcm/send/diwFCBIDLOaQ9NufdLMotZMWpEn5so-Ej6oOs2zjTW6Wzdq"
];

$auth = array(
    'VAPID' => array(
        'subject' => 'https://www.ninenik.com',
        'publicKey' => file_get_contents('./keys/public_key.txt'), // don't forget that your public key also lives in app.js
        'privateKey' => file_get_contents('./keys/private_key.txt'), // in the real world, this would be in a secret file
    ),
);

$webPush = new WebPush($auth);

$payload = [
	"message" => "This is test"
];
// 
foreach ($arr_endpoint as $endpoint) {
	$subscription = Subscription::create([
		"endpoint" => $endpoint,
		"contentEncoding" => "aesgcm",
	]);

    $webPush->queueNotification(
        $subscription,
        json_encode($payload)
    );
}

foreach ($webPush->flush() as $report) {
    $endpoint = $report->getRequest()->getUri()->__toString();
	if ($report->isSuccess()) {
		echo "[v] Message sent successfully for subscription {$endpoint}.";
	} else {
		echo "[x] Message failed to sent for subscription {$endpoint}: {$report->getReason()}";
	}
}
?>
    
    เมื่อรันคำสั่งก็จะได้ผลลัพธ์ดังนี้ กรณีส่งผ่านทั้งสองการแจ้งเตือน

 
 


 
 
    สามารถดูการปรับแต่งและใช้งาน การส่งการแจ้งเตือนเพิ่มเติมได้ที่
 
    หวังว่าแนวทางการใช้งาน Push  Notification นี้จะเป็นประโยชน์และสามารถนำไปประยุกต์ใช้งานต่อไป
ได้ไม่มากก็น้อย อย่าลืมดูเรื่องข้อจำกัดก่อนนำไปใช้งาน


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ







เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ