Gần đây mình có nghe một người bạn kể về một task trong công việc đó là làm sao để bắn thông báo cho ng dùng thông qua browser . Lúc đấy vs mình thì khái niệm này khá mơ hồ và mình cho rằng nếu đã tắt tab ứng dụng rồi thì làm sao bắn đc Notification chứ. Rồi mình thử tìm hiểu xem có thực sự làm đc chuyện đấy không . Bài viết này mình sẽ chia sẻ các thức hoạt động cũng như một ví dụ minh họa nhỏ mà mình tìm hiểu và làm được về Push notification .
À hóa ra là nó đây thứ mà bấy lâu nay mình nghĩ nó là quảng cáo và nhấn vào Block mà chẳng mảy may suy nghĩ nó thực sự là gì. Không dài dòng thêm nữa chúng ta cùng bắt đầu tìm hiểu về nó nào
Service Worker
Ủa ủa đang tìm hiểu về Push notification lên browser cơ mà. Vâng mình ko nhầm bài viết đâu ạ. Đầu tiên phải tìm hiểu Service Worker trước đã, Service Worker là một script hoạt động ngầm trên browser ( browser underground ) mà không có sự tương tác của người dùng . Ngoài ra, nó giống như một proxy hoạt động ở phía người dùng.
Chúng ta có thể làm gì với Service Worker?
- Kiểm soát Network Traffic!
- Cache lại dữ liệu req/res từ đó có thể chạy web offline
- Thực hiện một số tính năng chạy background như: Push notification và background synchronization.
- Service Worker chỉ có thể làm việc trên giao thức HTTPS. Bạn cũng có thể làm việc trên localhost trong quá trình phát triển.
- …
Trong bài này thì mình chỉ tập trung vào phần Push notification thôi nhé . Đầu tiên chúng ta cần nắm rõ hoạt động Push này có 2 phần:
- Push : việc gửi thông báo từ máy chủ đến ứng dụng. vì thế bạn sẽ cần một server
- Notification : thông báo được hiển thị trên thanh trạng thái của điện thoại hoặc trên browser.
Web push notifications flow
Chúng ta sẽ có 4 tác nhân chính trong quá trình này :
- User : Ng dùng muốn nhận thông báo
- Application : Ứng dụng chạy ở phía ng dùng
- Service worker : Chạy trên browser
- Push Server : Server bắn tin nhắn đến service workers
Giải thích flow :
1, Luồng bắt đầu khi ứng dụng yêu cầu người dùng đồng ý hiển thị notifications ( giống như ảnh ban đầu ý ). User sẽ có một vài kịch bản có thể xảy ra :
- Do nothing :
default
và như vậy thì notification sẽ ko hiện ra - Grant :
granded
notification sẽ có thể hiển thị - Deny :
denied
notification sẽ ko hiện ra
2, 3, Sau khi người dùng đồng ý nhận notifications ứng dụng sẽ register một service worker
4, 5, 6, Sau khi đăng kí thành công sẽ tiến hành tạo push notification subscription
7, Sau đó ứng dụng sẽ gửi đến server . Tất nhiên là server cần phải có 1 endpoint subscription để gửi noti
8, Sau đó server sẽ lưu subcription đó
9, Sau đó Serer có thể gửi notification đến service worker .
10, 11, 12, Service sẽ hiển thị cho ng dùng . Sau đó nếu ng dùng nhấn vào notification đấy server worker sẽ nhận được hành động đó thông quá notification click event. Sau đó nó có thể kích hoạt một số kịch bản như hiển thị trang web , gọi API , v..v….
Code tí vận động ngón tay nào
Ví dụ này mình sẽ làm bằng Nodejs và Reactjs vì thế mặc định là mọi người có một số kiến thức cũng như môi trường cài đặt nhé .
Tạo server
Vẫn những câu lệnh quen thuộc tạo một server nodejs + express và lần này có thêm một thư viện web-push
1 2 3 | npm init -y yarn add body-parser cors crypto express morgan web-push |
tạo file index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> bodyParser <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'body-parser'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> logger <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'morgan'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> cors <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'cors'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> subscriptionHandler <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./subscriptionHandler'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> port <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">4000</span><span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span> <span class="token function">cors</span><span class="token punctuation">(</span><span class="token punctuation">{</span> origin<span class="token punctuation">:</span> <span class="token string">'*'</span><span class="token punctuation">,</span> <span class="token comment">// allow to server to accept request from different origin</span> methods<span class="token punctuation">:</span> <span class="token string">'GET,HEAD,PUT,PATCH,POST,DELETE'</span><span class="token punctuation">,</span> credentials<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token comment">// allow session cookie from browser to pass through</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">logger</span><span class="token punctuation">(</span><span class="token string">'dev'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>bodyParser<span class="token punctuation">.</span><span class="token function">urlencoded</span><span class="token punctuation">(</span><span class="token punctuation">{</span> extended<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>bodyParser<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/subscription'</span><span class="token punctuation">,</span> subscriptionHandler<span class="token punctuation">.</span>handlePushNotificationSubscription<span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token string">'/subscription/:id'</span><span class="token punctuation">,</span> subscriptionHandler<span class="token punctuation">.</span>sendPushNotification<span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'The magic happens on port '</span> <span class="token operator">+</span> port<span class="token punctuation">)</span><span class="token punctuation">;</span> |
tạo file subscriptionHandler.js
để handle việc push notification phía server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <span class="token keyword">const</span> subscriptions <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">var</span> crypto <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'crypto'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> webpush <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'web-push'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> vapidKeys <span class="token operator">=</span> <span class="token punctuation">{</span> privateKey<span class="token punctuation">:</span> <span class="token string">'bdSiNzUhUP6piAxLH-tW88zfBlWWveIx0dAsDO66aVU'</span><span class="token punctuation">,</span> publicKey<span class="token punctuation">:</span> <span class="token string">'BIN2Jc5Vmkmy-S3AUrcMlpKxJpLeVRAfu9WBqUbJ70SJOCWGCGXKY-Xzyh7HDr6KbRDGYHjqZ06OcS3BjD7uAm8'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> webpush<span class="token punctuation">.</span><span class="token function">setVapidDetails</span><span class="token punctuation">(</span><span class="token string">'mailto:<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>'</span><span class="token punctuation">,</span> vapidKeys<span class="token punctuation">.</span>publicKey<span class="token punctuation">,</span> vapidKeys<span class="token punctuation">.</span>privateKey<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">createHash</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> md5sum <span class="token operator">=</span> crypto<span class="token punctuation">.</span><span class="token function">createHash</span><span class="token punctuation">(</span><span class="token string">'md5'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> md5sum<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>Buffer<span class="token punctuation">.</span><span class="token keyword">from</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> md5sum<span class="token punctuation">.</span><span class="token function">digest</span><span class="token punctuation">(</span><span class="token string">'hex'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">handlePushNotificationSubscription</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> subscriptionRequest <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>data<span class="token punctuation">;</span> <span class="token keyword">const</span> susbscriptionId <span class="token operator">=</span> <span class="token function">createHash</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>subscriptionRequest<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> subscriptions<span class="token punctuation">[</span>susbscriptionId<span class="token punctuation">]</span> <span class="token operator">=</span> subscriptionRequest<span class="token punctuation">;</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">201</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> susbscriptionId <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">sendPushNotification</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> subscriptionId <span class="token operator">=</span> req<span class="token punctuation">.</span>params<span class="token punctuation">.</span>id<span class="token punctuation">;</span> <span class="token keyword">const</span> pushSubscription <span class="token operator">=</span> subscriptions<span class="token punctuation">[</span>subscriptionId<span class="token punctuation">]</span><span class="token punctuation">;</span> webpush <span class="token punctuation">.</span><span class="token function">sendNotification</span><span class="token punctuation">(</span> pushSubscription<span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span> title<span class="token punctuation">:</span> <span class="token string">'New Product Available '</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">'HEY! Take a look at this brand new t-shirt!'</span><span class="token punctuation">,</span> image<span class="token punctuation">:</span> <span class="token string">'/images/jason-leung-HM6TMmevbZQ-unsplash.jpg'</span><span class="token punctuation">,</span> tag<span class="token punctuation">:</span> <span class="token string">'new-product'</span><span class="token punctuation">,</span> url<span class="token punctuation">:</span> <span class="token string">'/new-product-jason-leung-HM6TMmevbZQ-unsplash.html'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">202</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> handlePushNotificationSubscription<span class="token punctuation">,</span> sendPushNotification <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Kịch bản
Server này chúng ta tạo 2 api POST /subscription
và GET /subscription/:id
để đăng kí và kích hoạt push notification từ phía server tất nhiên là phần GET có thể đc kích hoạt bằng những xử lý logic trong project thực sự như khi có thêm sản phẩm mới ở web bán hàng chứ ko phải client gọi api để bắn notification về mình.
Hơn nữa mình cũng ko dùng database nào nên dữ liệu sẽ đc fix cứng trong function sendPushNotification
. Danh sách các client đăng kí cũng đc lưu trong obj subscriptions
và phân biệt vs nhau bằng id đc generate từ function createHash
Như vậy là đã xong phần server rồi .
Tạo React app
Cách nhanh nhất để tạo khởi tạo React là chạy lệnh create-react-app
:
1 2 3 4 | npx create-react-app frontend cd frontend yarn add axios serve |
Mình install luôn thư viện axios để tiện request đến server thay vì fetch
serve để chạy web trên file build
Chú ý
Mặc dù có thể chạy service worker ở local nhưng react yêu cầu phải chạy từ file build. Vì thế chạy service worker trên React thì phải chạy build rồi start bằng lệnh . Hơn nữa vì service worker có cache lại nên để test chuẩn nhất thì nên thỉnh thoảng xóa cache của browser .
1 2 | yarn serve -s build |
Tạo file usePushNotifications.js
( một custom Hook )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <span class="token keyword">import</span> <span class="token punctuation">{</span> useState<span class="token punctuation">,</span> useEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> serviceWorker <span class="token keyword">from</span> <span class="token string">'./serviceWorker'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> pushNotificationSupported <span class="token operator">=</span> serviceWorker<span class="token punctuation">.</span><span class="token function">isPushNotificationSupported</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// check push notifications are supported by the browser</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">usePushNotifications</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>userConsent<span class="token punctuation">,</span> setSuserConsent<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>Notification<span class="token punctuation">.</span>permission<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//to manage the user consent: Notification.permission is a JavaScript native function that return the current state of the permission</span> <span class="token comment">//We initialize the userConsent with that value</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>userSubscription<span class="token punctuation">,</span> setUserSubscription<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//to manage the use push notification subscription</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>pushServerSubscriptionId<span class="token punctuation">,</span> setPushServerSubscriptionId<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//to manage the push server subscription</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>error<span class="token punctuation">,</span> setError<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//to manage errors</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>loading<span class="token punctuation">,</span> setLoading<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//to manage async actions</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pushNotificationSupported<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//if the push notifications are supported, registers the service worker</span> <span class="token comment">//this effect runs only the first render</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> getExixtingSubscription <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> existingSubscription <span class="token operator">=</span> <span class="token keyword">await</span> serviceWorker<span class="token punctuation">.</span><span class="token function">getUserSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setUserSubscription</span><span class="token punctuation">(</span>existingSubscription<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">getExixtingSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//Retrieve if there is any push notification subscription for the registered service worker</span> <span class="token comment">// this use effect runs only in the first render</span> <span class="token comment">/** * define a click handler that asks the user permission, * it uses the setSuserConsent state, to set the consent of the user * If the user denies the consent, an error is created with the setError hook */</span> <span class="token keyword">const</span> <span class="token function-variable function">onClickAskUserPermission</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serviceWorker<span class="token punctuation">.</span><span class="token function">askUserPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>consent<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setSuserConsent</span><span class="token punctuation">(</span>consent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>consent <span class="token operator">!==</span> <span class="token string">'granted'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'Consent denied'</span><span class="token punctuation">,</span> message<span class="token punctuation">:</span> <span class="token string">'You denied the consent to receive notifications'</span><span class="token punctuation">,</span> code<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">//</span> <span class="token comment">/** * define a click handler that creates a push notification subscription. * Once the subscription is created, it uses the setUserSubscription hook */</span> <span class="token keyword">const</span> <span class="token function-variable function">onClickSusbribeToPushNotification</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serviceWorker <span class="token punctuation">.</span><span class="token function">createNotificationSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>subscrition<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setUserSubscription</span><span class="token punctuation">(</span>subscrition<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span> <span class="token string">"Couldn't create the notification subscription"</span><span class="token punctuation">,</span> err<span class="token punctuation">,</span> <span class="token string">'name:'</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span>name<span class="token punctuation">,</span> <span class="token string">'message:'</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span>message<span class="token punctuation">,</span> <span class="token string">'code:'</span><span class="token punctuation">,</span> err<span class="token punctuation">.</span>code <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">/** * define a click handler that sends the push susbcribtion to the push server. * Once the subscription ics created on the server, it saves the id using the hook setPushServerSubscriptionId */</span> <span class="token keyword">const</span> <span class="token function-variable function">onClickSendSubscriptionToPushServer</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios <span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'http://localhost:4000/subscription'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> userSubscription <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setPushServerSubscriptionId</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">/** * define a click handler that requests the push server to send a notification, passing the id of the saved subscription */</span> <span class="token keyword">const</span> onClickSendNotification <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`http://localhost:4000/subscription/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>pushServerSubscriptionId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setError</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> onClickAskUserPermission<span class="token punctuation">,</span> onClickSusbribeToPushNotification<span class="token punctuation">,</span> onClickSendSubscriptionToPushServer<span class="token punctuation">,</span> pushServerSubscriptionId<span class="token punctuation">,</span> onClickSendNotification<span class="token punctuation">,</span> userConsent<span class="token punctuation">,</span> pushNotificationSupported<span class="token punctuation">,</span> userSubscription<span class="token punctuation">,</span> error<span class="token punctuation">,</span> loading <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Nhiều code thế @@
- Đầu tiên là check browser có support push notification không
- Nếu push notifications đã được support tiến hành registers service worker
- Handler click bằng
onClickAskUserPermission
để yêu cầu ng dùng cung cấp quyền - Handler click bằng
onClickSubscribeToPushNotification
để tạo push notification subscription - Handler click bằng
onClickSendSubscriptionToPushServer
để gửipush subscription
đến server - Handler click bằng
onclickSendNotification
để mô phỏng việc server gửi notification cho mình
Tiếp theo thêm một vài function vào serviceWorker.js
file này đã đc tạo sẵn rồi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <span class="token keyword">const</span> pushServerPublicKey <span class="token operator">=</span> <span class="token string">'BIN2Jc5Vmkmy-S3AUrcMlpKxJpLeVRAfu9WBqUbJ70SJOCWGCGXKY-Xzyh7HDr6KbRDGYHjqZ06OcS3BjD7uAm8'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">askUserPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">await</span> Notification<span class="token punctuation">.</span><span class="token function">requestPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">createNotificationSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//wait for service worker installation to be ready</span> <span class="token keyword">const</span> serviceWorker <span class="token operator">=</span> <span class="token keyword">await</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span>ready<span class="token punctuation">;</span> <span class="token comment">// subscribe and return the subscription</span> <span class="token keyword">return</span> <span class="token keyword">await</span> serviceWorker<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> userVisibleOnly<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> applicationServerKey<span class="token punctuation">:</span> pushServerPublicKey <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">getUserSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//wait for service worker installation to be ready, and then</span> <span class="token keyword">return</span> navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span>ready <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>serviceWorker<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> serviceWorker<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">getSubscription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>pushSubscription<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> pushSubscription<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">isPushNotificationSupported</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator <span class="token operator">&&</span> <span class="token string">'PushManager'</span> <span class="token keyword">in</span> window<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Chú thích : chúng ta thêm các hàm
- askUserPermission để xin quyền của user
- isPushNotificationSupported để check browser có hỗ trợ ko ( cái này sẽ check đc ví dụ mở ẩn danh sẽ ko hỗ trợ )
- pushServerPublicKey của server
- createNotificationSubscription để tạo Notification Subscription mà chúng ta đã tạo sự kiện
onClickSusbribeToPushNotification
ởusePushNotifications.js
bên trên
Mấu chốt để bắn noti về browser
1 2 3 4 5 | <span class="token keyword">return</span> <span class="token keyword">await</span> serviceWorker<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> userVisibleOnly<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> applicationServerKey<span class="token punctuation">:</span> pushServerPublicKey <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Để tạo chúng ta cần 2 tham số
- userVisibleOnly : giá trị kiểu Boolean chỉ ra push subscription được server lại sẽ chỉ được sử dụng để hiển thị cho người dùng .
- applicationServerKey : là một Base64-encoded DOMString hoặc ArrayBuffer chứa ECDSA P-256 public key mà server push notification sau sẽ dùng để xác thực .
1 2 3 4 5 6 | endpoint<span class="token punctuation">:</span> <span class="token string">'.....'</span><span class="token punctuation">,</span> keys<span class="token punctuation">:</span> <span class="token punctuation">{</span> auth<span class="token punctuation">:</span> <span class="token string">'.....'</span><span class="token punctuation">,</span> p256dh<span class="token punctuation">:</span> <span class="token string">'.....'</span> <span class="token punctuation">}</span> |
Đây chính là những gì mà hàm createNotificationSubscription tạo ra và chúng ta dùng để register trên server.
- endpoint : là endpoint duy nhất mang tính chất duy nhất và được sử dụng để định tuyến tin nhắn mà server gửi đến đúng thiết bị .
- keys: dùng để xác thực và giải mã tin nhắn
Vậy còn khi server push thì phía browser sẽ nghe ở đâu ? . Đó chính là chúng ta phải ghi đè vào file service-worker.js . Thực ra file serviceWorker.js
mà create-react-app tạo sẵn ko phải là service-worker.js mà browser chạy ngầm mà sau khi build xong file đấy mới thực sự xuất hiện .
Vì thế chúng ta phải code thêm file sw.js
cùng cấp vs file serviceWorker.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <span class="token keyword">function</span> <span class="token function">receivePushNotification</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[Service Worker] Push Received.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> image<span class="token punctuation">,</span> tag<span class="token punctuation">,</span> url<span class="token punctuation">,</span> title<span class="token punctuation">,</span> text <span class="token punctuation">}</span> <span class="token operator">=</span> event<span class="token punctuation">.</span>data<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> url<span class="token punctuation">,</span> body<span class="token punctuation">:</span> text<span class="token punctuation">,</span> icon<span class="token punctuation">:</span> image<span class="token punctuation">,</span> vibrate<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">]</span><span class="token punctuation">,</span> tag<span class="token punctuation">:</span> tag<span class="token punctuation">,</span> image<span class="token punctuation">:</span> image<span class="token punctuation">,</span> badge<span class="token punctuation">:</span> <span class="token string">'https://spyna.it/icons/favicon.ico'</span><span class="token punctuation">,</span> actions<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> action<span class="token punctuation">:</span> <span class="token string">'Detail'</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'View'</span><span class="token punctuation">,</span> icon<span class="token punctuation">:</span> <span class="token string">'https://via.placeholder.com/128/ff0000'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>registration<span class="token punctuation">.</span><span class="token function">showNotification</span><span class="token punctuation">(</span>title<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">openPushNotification</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[Service Worker] Notification click Received.'</span><span class="token punctuation">,</span> event<span class="token punctuation">.</span>notification<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span>notification<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span>clients<span class="token punctuation">.</span><span class="token function">openWindow</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>notification<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'push'</span><span class="token punctuation">,</span> receivePushNotification<span class="token punctuation">)</span><span class="token punctuation">;</span> self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'notificationclick'</span><span class="token punctuation">,</span> openPushNotification<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Mục đích là để lắng nghe event và handle việc ng dùng click vào notification đấy. Sau đó sửa lại một chút phần file package.js
phần build và thêm lệnh sw ở dưới để nối file sw vào file service-worker.js
.
1 2 3 | <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"rm -rf build/ && react-scripts build && npm run-script sw"</span><span class="token punctuation">,</span> <span class="token property">"sw"</span><span class="token operator">:</span> <span class="token string">"cat src/sw.js >> build/service-worker.js"</span><span class="token punctuation">,</span> |
Cuối cùng là phần giao diện .Mình sẽ tận dụng luôn file App.js
làm demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> logo <span class="token keyword">from</span> <span class="token string">'./logo.svg'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> usePushNotifications <span class="token keyword">from</span> <span class="token string">'./usePushNotifications'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./App.css'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> userConsent<span class="token punctuation">,</span> pushNotificationSupported<span class="token punctuation">,</span> userSubscription<span class="token punctuation">,</span> onClickAskUserPermission<span class="token punctuation">,</span> onClickSusbribeToPushNotification<span class="token punctuation">,</span> onClickSendSubscriptionToPushServer<span class="token punctuation">,</span> pushServerSubscriptionId<span class="token punctuation">,</span> onClickSendNotification<span class="token punctuation">,</span> error<span class="token punctuation">,</span> loading <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">usePushNotifications</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Loading</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> loading <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> loading <span class="token operator">?</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'app-loader'</span><span class="token operator">></span>Please wait<span class="token punctuation">,</span> we are loading something<span class="token operator">...</span><span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Error</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> error <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> error <span class="token operator">?</span> <span class="token punctuation">(</span> <span class="token operator"><</span>section className<span class="token operator">=</span><span class="token string">'app-error'</span><span class="token operator">></span> <span class="token operator"><</span>h2<span class="token operator">></span><span class="token punctuation">{</span>error<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Error message <span class="token punctuation">:</span> <span class="token punctuation">{</span>error<span class="token punctuation">.</span>message<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Error code <span class="token punctuation">:</span> <span class="token punctuation">{</span>error<span class="token punctuation">.</span>code<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>section<span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">const</span> isConsentGranted <span class="token operator">=</span> userConsent <span class="token operator">===</span> <span class="token string">'granted'</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'App'</span><span class="token operator">></span> <span class="token operator"><</span>header className<span class="token operator">=</span><span class="token string">'App-header'</span><span class="token operator">></span> <span class="token operator"><</span>img src<span class="token operator">=</span><span class="token punctuation">{</span>logo<span class="token punctuation">}</span> className<span class="token operator">=</span><span class="token string">'App-logo'</span> alt<span class="token operator">=</span><span class="token string">'logo'</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>Loading loading<span class="token operator">=</span><span class="token punctuation">{</span>loading<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Push notification are <span class="token punctuation">{</span><span class="token operator">!</span>pushNotificationSupported <span class="token operator">&&</span> <span class="token string">'NOT'</span><span class="token punctuation">}</span> supported by your device<span class="token punctuation">.</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span> User consent to recevie push notificaitons is <span class="token operator"><</span>strong<span class="token operator">></span><span class="token punctuation">{</span>userConsent<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>strong<span class="token operator">></span><span class="token punctuation">.</span> <span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>Error error<span class="token operator">=</span><span class="token punctuation">{</span>error<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>button disabled<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">!</span>pushNotificationSupported <span class="token operator">||</span> isConsentGranted<span class="token punctuation">}</span> onClick<span class="token operator">=</span><span class="token punctuation">{</span>onClickAskUserPermission<span class="token punctuation">}</span><span class="token operator">></span> <span class="token punctuation">{</span>isConsentGranted <span class="token operator">?</span> <span class="token string">'Consent granted'</span> <span class="token punctuation">:</span> <span class="token string">' Ask user permission'</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span>button disabled<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">!</span>pushNotificationSupported <span class="token operator">||</span> <span class="token operator">!</span>isConsentGranted <span class="token operator">||</span> userSubscription<span class="token punctuation">}</span> onClick<span class="token operator">=</span><span class="token punctuation">{</span>onClickSusbribeToPushNotification<span class="token punctuation">}</span><span class="token operator">></span> <span class="token punctuation">{</span>userSubscription <span class="token operator">?</span> <span class="token string">'Push subscription created'</span> <span class="token punctuation">:</span> <span class="token string">'Create Notification subscription'</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span>button disabled<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">!</span>userSubscription <span class="token operator">||</span> pushServerSubscriptionId<span class="token punctuation">}</span> onClick<span class="token operator">=</span><span class="token punctuation">{</span>onClickSendSubscriptionToPushServer<span class="token punctuation">}</span><span class="token operator">></span> <span class="token punctuation">{</span>pushServerSubscriptionId <span class="token operator">?</span> <span class="token string">'Subscrption sent to the server'</span> <span class="token punctuation">:</span> <span class="token string">'Send subscription to push server'</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token punctuation">{</span>pushServerSubscriptionId <span class="token operator">&&</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>The server accepted the push subscrption<span class="token operator">!</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span>onClickSendNotification<span class="token punctuation">}</span><span class="token operator">></span>Send a notification<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>header<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> |
nhớ cuối cùng là chạy lệnh :
1 2 3 | yarn build yarn serve -s build |
Demo : sau khi nhấn 3 nút từ trên xuống dưới và nhấn nút cuối cùng để kích hoạt bắn notification về browser
Link github : https://github.com/vinhyenvodoi98/Push_Notification_Nodejs_Reactjs
Reference :
- https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications
- https://itnext.io/react-push-notifications-with-hooks-d293d36f4836
- https://itnext.io/an-introduction-to-web-push-notifications-a701783917ce
- https://stackoverflow.com/questions/57185722/how-to-add-event-listeners-to-create-react-app-default-sw-js-file/57185956