8

I am trying to display notifications with jQuery locally on page load. The notification is showing correctly in Firefox, Firefox Developer, and Chrome. The notification is not appearing in Safari despite allowed in notification preference settings.

Similar code is working from MDN site https://developer.mozilla.org/en/docs/Web/API/notification.

Snippet is below.

// Display a sample notification
  if (window.Notification) {
    return $(".au-notifications-page").show(function() {
      var notification;
      notification = new Notification(
        'Success Text', {
        //tag: $("[name=tag]").val(),
        body: 'Success Message',
        iconUrl: 'img/avatar-male.png',
        icon: 'img/avatar-male.png'
      });
      return notification.onclick = function() {
        notification.close();
        window.open().close();
        return window.focus();
      };
    });
  };

Complete code is below.

$(document).ready(function () {

  // Request permission on site load
  Notification.requestPermission().then(function(result) {
    if (result === 'denied') {
      //alert('denied');
      $(".au-notif-disabled-header").removeClass('hide');
      $(".au-notif-disabled-header .btn").addClass('hide');
      return;
    }
    if (result === 'default') {
      //alert('ignored');
      $(".au-notif-disabled-header").removeClass('hide');
      return;
    }
    //alert('granted');
    $(".au-notif-disabled-header").addClass('hide');
  });

  // Request permission with button
  $('.au-notif-disabled-header .btn').click(function () {
    Notification.requestPermission().then(function(result) {
      if (result === 'denied') {
        $(".au-notif-disabled-header").removeClass('hide');
        $(".au-notif-disabled-header .btn").addClass('hide');
        return;
      }
      if (result === 'default') {
        $(".au-notif-disabled-header").removeClass('hide');
        return;
      }
      $(".au-notif-disabled-header").addClass('hide');
    });
  });

  $( ".au-notification-icon" ).hover(
    function() {
      $(".au-notifications-menu .au-notif-msg-realtime").slideDown();
      $('.au-notification-icon .badge').html("2");
    }, function() {
      $(".au-notifications-menu .au-notif-msg-realtime").slideUp();
      $('.au-notification-icon .badge').html("1");
    }
  );

  //To show notification received while on notifications page
  $(".au-notif-msg-realtime").hide();
  //$(".au-notifications-page .au-notif-msg-realtime").slideDown();

  $(".au-notifications-page .au-notif-msg-realtime").slideDown({
    complete: function(){
      $('.au-notification-icon .badge').html("2");
      $('head title').html("(2) Notifications");
    }
  });


  // Display a sample notification
  if (window.Notification) {
    return $(".au-notifications-page").show(function() {
      var notification;
      notification = new Notification(
        'Success Heading', {
          body: 'Success Text',
          iconUrl: 'img/avatar-male.png',
          icon: 'img/avatar-male.png'
      });
      return notification.onclick = function() {
        notification.close();
        window.open().close();
        return window.focus();
      };
    });
  };
});

EDIT 1: Safari throws this exception

undefined is not an object (evaluating 'Notification.requestPermission().then')

Machavity
  • 28,730
  • 25
  • 78
  • 91
Ahmed
  • 81
  • 1
  • 6

3 Answers3

16

You have to use a callback function for Safari, since it doesn't return a Promise.

According to MDN:

This uses the promise-version of the method, as supported in recent implementations (Firefox 47, for example.) If you want to support older versions, you might have to use the older callback version, which looks like this:

Here's the sample code they gave:

Notification.requestPermission(function (permission) {
    // If the user accepts, let's create a notification
    if (permission === "granted") {
        var notification = new Notification("Hi there!");
    }
});

To support Safari notifications, this is what I ended up with:

    try {
        Notification.requestPermission()
            .then(() => doSomething())                                                                                                                                               
    } catch (error) {
        // Safari doesn't return a promise for requestPermissions and it                                                                                                                                       
        // throws a TypeError. It takes a callback as the first argument                                                                                                                                       
        // instead.
        if (error instanceof TypeError) {
            Notification.requestPermission(() => {                                                                                                                                                             
                doSomething();
            });
        } else {
            throw error;                                                                                                                                                                                       
        }                                                                                                                                                                                                      
    }      
Tsunamis
  • 4,792
  • 1
  • 16
  • 21
zaizhuang
  • 177
  • 1
  • 7
  • 1
    As some browsers (i.e. Safari on iOS 11.4) don't support `Notification` it might be a good practice to check `typeof Notification !== 'undefined'` before executing the try/catch block. – Tsunamis Aug 10 '18 at 10:08
10

A better solution is to wrap the results in a Promise and then (no pun intended) run your code. This code works on all browsers (including Safari) and without a complicated if block (concept is discussed in detail in this question)

Promise.resolve(Notification.requestPermission()).then(function(permission) {
    // Do something
});

This works because Promise.resolve does nothing to a Promise, but will convert the Safari requestPermission() to a Promise.

Note that iOS Safari still does not support the Notification API so you will need to check if it is available first

Machavity
  • 28,730
  • 25
  • 78
  • 91
  • On safari, permission will be undefined on Safari, right? – besserwisser Aug 20 '20 at 08:02
  • `permission` is merely the argument name I picked. The promise will pass the notification permission object to the callback function – Machavity Aug 20 '20 at 11:22
  • This doesn't work, because `Notification.requestPermission()` immediately returns `undefined` on Safari, the `then` function will be called instantly, without waiting for the permission request dialog to be resolved, and regardless of what the user chooses. – Sam Barnum Feb 09 '21 at 20:15
0

To return a promise which doesn't resolve until the user grants or denies permission to show notifications:

        if (!permissionPromise && Notification.permission === 'granted' ) {
            permissionPromise = Promise.resolve(Notification.permission);
        }
        if (!permissionPromise) {
            permissionPromise = new Promise(function (resolve, reject) {
                // Safari uses callback, everything else uses a promise
                var maybePromise = $window.Notification.requestPermission(resolve, reject);
                if (maybePromise && maybePromise.then) {
                    resolve(maybePromise);
                }
            });
        }
        return permissionPromise;
Sam Barnum
  • 9,932
  • 3
  • 48
  • 57