點燈坊

學而時習之,不亦悅乎

使用 HTML 5 的 Notification API

Sam Xiao's Avatar 2019-09-12

有些網頁會在右側出現小視窗,事實上這是 HTML 5 所提供 Notification,由 Browser 所提供,可直接使用。

Version

macOS Mojave 10.14.6
Safari 12.1.2
Chrome 76.0.3809.132
Firefox Developer Edition 70.0b4
WebStorm 2019.2.1
Node 12.10
Vue CLI 3.11.0
Vue 2.6.10

Browser Compatibility

let supportNotification = () => ('Notification' in window);

若 browser 有支援 notification,則 window object 會有 Notification property,因此可自行設計 supportNotification() 判斷 browser 是否支援 notification。

Getting Permisstion

Callback

Notification.requestPermission(console.log);

要使用 notification 前提,首先必須使用 Notification.requestPermission() 取得 user 的允許,可傳入 callback function 取得 permission。

  • default:還未取得 permission
  • granted:user 允許使用 notification
  • denied:user 拒絕使用 notification

Promise

Notification.requestPermission().then(console.log);

Notification.requestPermission() 亦會回傳 Promise,也可在 then() 傳入 callback function 取得。

Creating Notification

Async Await

let supportNotification = () => ('Notification' in window);

let mounted = async function() {
  if (!supportNotification()) return;

  let permission = await Notification.requestPermission();

  if (permission === 'granted') {
    return new Notification('Sam', { body: 'Hello World' });
  }
};

若 user 允許使用 notification,則可進一步建立 notification。

由於 Notification.requestPermission() 回傳為 Promise,可使用 await 建立 permission 變數,如此可使用 imperative 的 if 判斷是否為 granted

Notification constructor 建立 notification:

  • Sam:為 title 部分
  • { body: 'Hello World' }:為 body 部分

Promise

let supportNotification = () => ('Notification' in window);

let mounted = function() {
  if (!supportNotification()) return;

  Notification.requestPermission()
    .then(x => {
      if (x === 'granted') {
        return new Notification('Sam', { body: 'Hello World'});
      }
    });
};

由於 Notification.requestPermission() 回傳為 Promise,也可使用 then() 取得 permission,並將 if() 判斷寫在 then() 的 callback 內。

Extract Function

let createNotification = x => {
  if (x === 'granted') {
    return new Notification('Sam', { body: 'Hello World'});
  }
};

let mounted = function() {
  if (!supportNotification()) return;

  Notification.requestPermission()
    .then(createNotification);
}

很明顯在 then() 的 callback 寫大量邏輯並不好閱讀,將其 extract 成獨立 function 後就好多了,也符合 SRP 原則。

Pipeline

import { pipe, when, equals, then } from 'ramda';
import { iif } from 'wink-fp';

let supportNotification = () => ('Notification' in window);

let createNotification = pipe(
  when(equals('granted')),
  () => new Notification('Sam', { body: 'Hello World' })
);

let mounted = pipe(
  supportNotification
  iif(Notification.requestPermission),
  then(createNotification)
);

其實觀察 mounted()createNotification() 可發現,這兩者都有 pipeline 本質:

  • mounted():先使用 supportNotification() 判斷是否支援 notification,再呼叫 Notification.requestPermission() 對 user 要求 permission,最後由 Promise 建立 notification
  • createNotification():先判斷 permission 是否為 granted,在使用 Notification constructor 建立 notification

11 行

let mounted = pipe(
  supportNotification
  iif(Notification.requestPermission),
  then(createNotification)
);

先使用 supportNotification() 回傳 truefalse,若為 true 則執行 Wink-fp 的 iif()Notification.requestPermission(),否則不執行中斷 pipeline。

由於 Notification.requestPermission() 回傳為 Promise,使用 Ramda 的 then()createNotification() 串起來。

第 6 行

let createNotification = pipe(
  when(equals('granted')),
  () => new Notification('Sam', { body: 'Hello World' })
);

使用 Ramda 的 when() 判斷 permission 是否為 granted,若成立則繼續使用 Notification constructor 建立 notification。

Function Composition

import { compose, when, equals, then } from 'ramda';
import { iif } from 'wink-fp';

let supportNotification = () => ('Notification' in window);

let createNotification = compose(
  () => new Notification('Sam', { body: 'Hello World' }),
  when(equals('granted'))
);

let mounted = compose(
  then(createNotification),
  iif(Notification.requestPermission),
  supportNotification
);

既然能使用 pipe() 達成 pipeline,反向使用 compose() 就是 function composition 了。

Conclusion

  • 若只要學習如何使用 HTML 5 的 notification api,看到 async await 即可,後面是用 Notification.requestPermission() 回傳 Promise 特性繼續重構
  • 若使用 async await,由於其 imperative 特性,可發現重構到一個階段就停止了;但若使用 then(),由於其 function 與 pipeline 本質,可繼續往下重構成 function composition

Reference

MDN, Using the Notification API
Ben Kennish, HTML 5 Web Notification Test