點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Notification 提供通知

Sam Xiao's Avatar 2020-12-31

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

Version

HTML 5

Notification

notification000

詢問 user 是否允許 notification。

<template>
  <div>HTML Notification</div>
</template>

<script>
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' })
}

export default {
  mounted
}
</script>

第 6 行

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

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

11 行

let permission = await Notification.requestPermission()

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

若要使用 notification,首先必須使用 Notification.requestPermission() 取得 user 的允許,其回傳為 Promise,可傳入 callback 取得 permission。

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

Notification constructor 建立 notification:

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

Promise Chain

<template>
  <div>HTML Notification</div>
</template>

<script>
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' })
    })
}

export default {
  mounted
}
</script>

11 行

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

也可使用 Promise Chain 在 then() 的 callback 中判斷。

<template>
  <div>HTML Notification</div>
</template>

<script>
let supportNotification = () => ('Notification' in window)

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

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

export default {
  mounted
}
</script>

11 行

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

也可使用多個 then() 讓 Promise Chain 好看些。

Function Pipeline

<template>
  <div>HTML Notification</div>
</template>

<script>
import { pipe, bind, andThen as then, equals, when } from 'ramda'

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

let requestPermission = bind(Notification.requestPermission, Notification)

let createNotification = (title, options) => new Notification(title, { body: options })

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

  pipe(
    requestPermission,
    then(equals('granted')),
    then(when(equals(true), createNotification('Sam', 'Hello World')))
  )()
}

export default {
  mounted
}
</script>

10 行

let requestPermission = bind(Notification.requestPermission, Notification)

使用 bind()Notification.requestPermission() 抽出 requestPermission() free function。

12 行

let createNotification = (title, options) => new Notification(title, { body: options })

使用 createNotification() 建立 Notification Object。

17 行

pipe(
  requestPermission,
  then(equals('granted')),
  then(when(equals(true), createNotification('Sam', 'Hello World')))
)()

pipe() 中使用 Promise Chain。

<template>
  <div>HTML Notification</div>
</template>

<script>
import { pipe, bind, andThen as then, equals } from 'ramda'
import { iif } from 'wink-fp'

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

let requestPermission = bind(Notification.requestPermission, Notification)

let createNotification = (title, options) => pipe(
  equals('granted'),
  iif(() => new Notification(title, { body: options }))
)

let mounted = pipe(
  supportNotification,
  iif(requestPermission),
  then(createNotification('Sam', 'Hello World'))
)

export default {
  mounted
}
</script>

13 行

let createNotification = (title, options) => pipe(
  equals('granted'),
  iif(() => new Notification(title, { body: options }))
)

也可使用 Wink-fp 的 iif() 取代 when(equals(true)),可讀性更高。

18 行

let mounted = pipe(
  supportNotification,
  iif(requestPermission),
  then(createNotification('Sam', 'Hello World'))
)

直接使用 pipe() 組合出 mounted()

Conclusion

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

Reference

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