4.8 KiB
Server Notifications
About
This is the clients equivalent of server push notifications. The ServerNotificationsService takes
care of receiving the notifications from our server no matter the technology needed to make it
happen behind the scenes.
Usage
At the current moment the best way to consume a notification pushed to our client is by adding code
to the switch statement in the processNotification method of
DefaultServerNotificationsService. It's best
to put as much of your logic in your own service and inject and call a method on that service in
response to your notification type being processed.
In the future, notifications will be able to be handled in response to the notifications$
observable. This stream will contain all notifications that come from our server and it will be
your responsibility to filter out for only the notifications you care about and to parse the payload
into your expected type. Through this stream you will also be required to filter out if the
notification should not be handled as the current device was the originator of the notification
or if you only want to handle the notifications of the currently active user.
Implementation
There are three server notification service implementations available for our clients, with specific use cases detailed below.
Default
The default implementation is the main implementation that actually does the actions people expect
it to do. This service manages the logic for when we should connect, ensuring we always have a
connection during those times, and trying to limit reconnection events to only when necessary.
The service establishes a connection for the active user if they have an available access token and
a notification URL other than http://-, which is used as a special value to say that notifications
should not be used. Then the service will reach out to the injected WebPushConnectionService for
its support status on if it supports web push. If it does support web push it will give us an object
to use to connect to web push notifications. If that service tells us web push is not supported or
any exceptions happen in the web push stream we fall back to connecting to SignalR notifications. We
are sure to use the rxjs operator distinctUntilChanged on a lot of these events to help avoid
doing unnecessary reconnects.
This structure allows us to inject a different implementation of WebPushConnectionService based on
the client, depending on the best way to use web push in that client's ecosystem. For now it is only
using the Service Worker of our Chrome MV3 extension. Possible future implementation of this service
could be a Worker in our web app. This would require that we request the Notification permission
and that browsers start allowing userVisibleOnly: false. Another possible implementation that
could apply to web and desktop would be using our autopush-manager package. That package uses web
sockets under the hood but implements the cryptography layer of web push on top of it so that we can
offload the socket connection to another party.
The injected SignalRConnectionService is another service that we can utilize to make the below
foreground service not needed anymore. Instead of having a whole different
ServerNotificationService implementation we can have a new implementation of this and it can defer
to the service worker for the more persistent connection but send SignalR messages through
MessageSender. Since web push shouldn't have any problem of creating multiple connection per
browser context this brings the solution closer to the actual problem.
Noop
This is a special implementation that can be opted into being used through the dev flag
noopNotifications. When that flag is true then the noop version of server notifications will be
used. This is usually done to keep the console cleaner from any logs that might happen from the
default implementation. We could, in the future decide to depracate this implementation in favor
of instructing users to use http://- as their notifications url in the local.json configuration.
That should largely have the same behavior and would allow us to maintain one fewer implementation.
Foreground
The foreground implementation is specially for browser foreground instances. At the moment this
service acts as a stub to avoid accidentally doubling up SignalR connections. If we had the default
implementation in both the background service worker and in each foreground instance anyone then
would be a web socket connection made in each. With this special instance we avoid that by doing
nothing at the moment. Once we begin to fully support the notifications$ observable we can make
a choice whether or not keep not supporting it in the browser foreground or we can decide to support
it through messaging the background.