Created
January 30, 2015 12:11
-
-
Save teebot/a298756f12f474f9447e to your computer and use it in GitHub Desktop.
A desktop notification angular service that handles browser permissions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This service is a wrapper for the Notification API | |
# It ensures only one notification is shown at a time | |
angular.module('acmeServices').factory 'notificationService', ($window, $timeout, userPrefsService) -> | |
ALREADY_PROMPTED_UP_KEY = 'notifAlreadyPrompted' | |
LAST_NOTIF_TIME_UP_KEY = 'lastNotifTime' | |
NOTIF_GRANTED_STATE = 'granted' | |
NOTIF_DENIED_STATE = 'denied' | |
NOTIF_TIMEOUT_MS = 20000 | |
MIN_TIME_BETWEEN_NOTIF = 2000 | |
notificationAllowed = $window.Notification?.requestPermission && $window.Notification.permission != NOTIF_DENIED_STATE | |
currentNotification = null | |
confirmCallback = null | |
promptCallback = null | |
dismissCallback = null | |
if $window.Audio? | |
beepSound = new $window.Audio('/wav/beep.wav') | |
init = (promptCallbackP, confirmCallbackP, dismissCallbackP) -> | |
confirmCallback = confirmCallbackP | |
promptCallback = promptCallbackP | |
dismissCallback = dismissCallbackP | |
if notificationAllowed | |
alreadyPrompted = userPrefsService.get(ALREADY_PROMPTED_UP_KEY) || false | |
if !hasPermission() && !alreadyPrompted | |
promptCallback() | |
return | |
hasPermission = -> | |
return $window.Notification.permission == NOTIF_GRANTED_STATE | |
doNotPromptMeFuture = -> | |
userPrefsService.set(ALREADY_PROMPTED_UP_KEY, true) | |
return | |
requestPermission = -> | |
requestPermissionCallback = -> | |
if hasPermission() | |
confirmCallback() | |
else if $window.Notification.permission == NOTIF_DENIED_STATE | |
dismissCallback() | |
else | |
promptCallback() | |
return | |
$window.Notification.requestPermission(requestPermissionCallback) | |
return | |
show = (notificationText) -> | |
lastNotifTime = userPrefsService.get(LAST_NOTIF_TIME_UP_KEY) | |
# ignore notification if the last one was too recent | |
if lastNotifTime? && (($window.Date.now() - lastNotifTime) < MIN_TIME_BETWEEN_NOTIF) | |
return | |
userPrefsService.set(LAST_NOTIF_TIME_UP_KEY, $window.Date.now()) | |
currentNotification.close() if currentNotification | |
if hasPermission() | |
beepSound.play() if beepSound? | |
currentNotification = new Notification 'acme', { body: notificationText, icon: '/img/icon.png', tag: 'acme' } | |
currentNotification.onclick = -> | |
# TODO: should take you to an optional link provided on the notify method | |
$window.focus() | |
currentNotification.close() | |
# auto-close | |
$timeout( | |
-> | |
currentNotification.close() if currentNotification | |
NOTIF_TIMEOUT_MS | |
) | |
return | |
return { | |
init: init | |
requestPermission: requestPermission | |
doNotPromptMeFuture: doNotPromptMeFuture | |
show: show | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module 'windowMock', [] | |
angular.module('windowMock').service '$window', -> | |
@permissionCallback = null | |
return { | |
Notification: { | |
requestPermission: (permissionCallback) -> | |
@permissionCallback = permissionCallback | |
return null | |
permission: 'default' | |
} | |
Date: { | |
now: -> | |
return window.Date.now() | |
} | |
} | |
describe 'notification service', -> | |
notificationService = null | |
userPrefsService = null | |
promptCallbackCalled = false | |
window_ = null | |
beforeEach(module('acmeServices', 'windowMock')) | |
beforeEach(inject( (_notificationService_, _$window_, _userPrefsService_) -> | |
notificationService = _notificationService_ | |
window_ = _$window_ | |
userPrefsService = _userPrefsService_ | |
)) | |
it 'should trigger prompt callback when notification is possible', -> | |
notificationService.init(-> | |
promptCallbackCalled = true | |
) | |
expect(promptCallbackCalled).toEqual(true) | |
it 'should trigger browser request permission with a callback', -> | |
notificationService.init(-> | |
return null | |
) | |
notificationService.requestPermission() | |
expect(typeof window_.Notification.permissionCallback).toEqual('function') | |
it 'should store do not ask me in the future', -> | |
notificationService.init(-> | |
return null | |
) | |
notificationService.doNotPromptMeFuture() | |
noPromptInFuture = userPrefsService.get('notifAlreadyPrompted') | |
expect(noPromptInFuture).toEqual(true) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The user prefs service dependency is in my previous gist