Search code examples
ruby-on-railsweb-push

Identifying users in Rails Web push notifications


I'm developing a Rails application, and I'd like to send web push notifications to specific users when certain actions happen, e.g:

A user started tracking a timer, but the timer has been running for more than 6 hours. Then the app sends that user a web notification.

I've been doing research and found this tutorial, the author implements push notifications for Rails, however there's no insight on how to identify the users.

From what I understood, the users needs to subscribe from their browser to be able to get push notifications, however, considering each user can use the application from multiple browsers, how can I automatically subscribe/unsubscribe a user for notifications in all browsers they use the app from?


Solution

  • So, what I did was adding a notification_subscription model to my User model.

    On my javascript, I check if there's a current browser subscription present:

    this.serviceWorkerReady()
      .then((serviceWorkerRegistration) => {
         serviceWorkerRegistration.pushManager.getSubscription()
         .then((pushSubscription) => {
            if (pushSubscription && _.includes(subscriptionEndpoints, pushSubscription.endpoint)) {
              return;
         }
    
         this.subscribe();
      });
    });
    

    I check if the current subscription is already present in the user stored endpoints, and subscribe if it isn't.

    On subscription I send the new endpoint to the backend, which adds the new subscription to the user:

    $.post(Routes.users_subscriptions_path({ format: 'json' }), {
      subscription: subscription.toJSON()
    });
    

    Then I can send notifications to users to every endpoint:

    def push_notifications_to_user(user)
        message = {
          title: "A message!",
          tag: 'notification-tag'
        }
    
        user.notification_subscriptions.each do |subscription|
          begin
            Webpush.payload_send(
              message: JSON.generate(message),
              endpoint: endpoint,
              p256dh: p256dh,
              auth: auth,
              api_key: public_key
            )
          rescue Webpush::InvalidSubscription => exception
            subscription.destroy
          end
        end
      end
    

    The webpush gem raises an InvalidSubscription exception if the endpoint is invalid, we can destroy that endpoint to keep only the valid endpoints from the user.