Preface
I recently wanted to use the Notification API to show desktop notifications for another project I’m working on. Even though the API itself is pretty easy to use, there were a few issues I had to resolve as well as some reasonable defaults I chose to make the whole thing as easy as possible to use.
While the new Notification()
constructor is supported in all desktop browsers, only Firefox seems to support this way of showing notifications on Android, Edge and Chrome don’t. On the iOS side, Safari seems to not really support this as well.
The solution here would be to implement a service worker, from which notifications are supported on all platforms. This would involve an external .js file with the worker code to be loaded by the api, wich I felt would go beyond the scope of this simple wrapper.
Regarding the issues:
It seems that the js.window
API doesn’t support static methods or properties like Array.from
, String.fromCodePoint
or in my case Notification.requestPermission
/Notification.permission
. I solved this by window.eval
ing the method call, but this felt like a pretty hacky workaround where proper support for static members would have been nicer.
Implementation
notification = {}
function notification.currentPermission()
-- static properties seem to be inaccessible with js.window translation
return js.window.eval("Notification.permission")
end
function notification.requestPermission()
local current = notification.currentPermission()
if current == "default" then
return js.window.eval("Notification.requestPermission()")
else
return current
end
end
-- this way, the table can easily be extended/overwritten
notification.levels = {info = "ℹ️", warn = "⚠️", caution = "❗", urgent = "🔔", tip = "💡"}
-- for options see https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification#parameters
function notification.show(title, options)
if not title then error("notification title required!") end
if options and not options.icon then
-- set options.icon to "#" (any invalid url) to prevent the 'default' icon
options.icon = js.window.location.origin + "/.client/logo-dock.png"
end
-- not part of the standard api
if options and options.level and #options.level != 0 then
local e = notification.levels[options.level]
options.icon = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"> <text y=".9em" font-size="54">'..e..'</text> </svg>'
end
local current = notification.currentPermission()
if current == "granted" then
return js.new(js.window.Notification, title, options)
else if current == "default" then
-- ask for notification permission in first call
if notification.requestPermission() == "granted" then
notification.show(title, options)
end
end end
end
The options.level
field is not part of the standard API, but I thought it would be nice to have some MessageBox
-like standard icons without having to provide an url each time.
Example
${widgets.button("Show Notification", function()
notification.show("Fizz", {body = "Buzz", requireInteraction = true, level = "tip"})
end)}