Skip to content

Instantly share code, notes, and snippets.

@teebot
Created January 30, 2015 12:11
Show Gist options
  • Save teebot/a298756f12f474f9447e to your computer and use it in GitHub Desktop.
Save teebot/a298756f12f474f9447e to your computer and use it in GitHub Desktop.
A desktop notification angular service that handles browser permissions
# 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
}
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)
@teebot
Copy link
Author

teebot commented Jan 30, 2015

The user prefs service dependency is in my previous gist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment