Easy desktop notifications for silverbullet

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.windowAPI 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.evaling 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)}
4 Likes

This is pretty cool! I wonder what the use case is though? Maybe in combination with the cron event? Otherwise, I imagine the SilverBullet notifications are sufficient, since they would only appear when you are active in SilverBullet.

I could see this as a way to show reminders for tasks on their due date at a set time. You would just need the tab open but I could be working somewhere else and get the notification. Now if we could just support timestamps for tasks then we could get a notification, say 15 mins before it is due.

1 Like

Both of you are exactly right! :smiley:
I was planning to implement some kind of notification system for (over-)due tasks with the cron event. Right now, without support for attribute extractors, the only thing I can think of would be notifications for due tasks in a certain number of days, but hour or minute level would be much nicer!

Edit: Regarding the use case: I’ve got silverbullet open all the time in a pinned browser-tab, so notifications could be received even with the browser minimized.

2 Likes

Same for me.