Mailspring/internal_packages/unread-notifications/lib/main.coffee
Ben Gotow 29cc5853b3 feat(notifs): Native notifications for Mac OS X, Win coming
Summary: Replaces `new Notification`-based HTML5 notifications with system native notifications on Mac OS X. This allows us to implement the "Reply" button in the notifications. This will also serve as the hook for native Windows notifications, which are unsupported in Chromium.

Test Plan: Run tests

Reviewers: evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D2199
2015-10-29 11:23:51 -07:00

139 lines
4.5 KiB
CoffeeScript

_ = require 'underscore'
{Thread,
Actions,
MailViewFilter,
AccountStore,
CategoryStore,
SoundRegistry,
NativeNotifications,
DatabaseStore} = require 'nylas-exports'
module.exports =
config:
enabled:
type: 'boolean'
default: true
activate: ->
@unlisteners = []
@unlisteners.push Actions.didPassivelyReceiveNewModels.listen(@_onNewMailReceived, @)
@activationTime = Date.now()
@stack = []
@stackProcessTimer = null
deactivate: ->
fn() for fn in @unlisteners
serialize: ->
_notifyAll: ->
NativeNotifications.displayNotification
title: "#{@stack.length} Unread Messages",
tag: 'unread-update'
@stack = []
_notifyOne: ({message, thread}) ->
account = _.find AccountStore.items(), (a) -> a.id is message.accountId
from = message.from[0]?.displayName() ? "Unknown"
title = from
if message.subject and message.subject.length > 0
subtitle = message.subject
body = message.snippet
else
subtitle = message.snippet
body = null
notif = NativeNotifications.displayNotification
title: title
subtitle: subtitle
body: body
canReply: true
tag: 'unread-update'
onActivate: ({tag, response, activationType}) =>
if activationType is 'replied' and response and _.isString(response)
Actions.sendQuickReply({thread, message}, response)
else
atom.displayWindow()
if AccountStore.current().id isnt thread.accountId
Actions.selectAccountId(thread.accountId)
MailViewFilter filter = MailViewFilter.forCategory(thread.categoryNamed('inbox'))
Actions.focusMailView(filter)
Actions.setFocus(collection: 'thread', item: thread)
_notifyMessages: ->
if @stack.length >= 5
@_notifyAll()
else if @stack.length > 0
@_notifyOne(@stack.pop())
@stackProcessTimer = null
if @stack.length > 0
@stackProcessTimer = setTimeout(( => @_notifyMessages()), 2000)
# https://phab.nylas.com/D2188
_onNewMessagesMissingThreads: (messages) ->
setTimeout =>
threads = {}
for msg in messages
threads[msg.threadId] ?= DatabaseStore.find(Thread, msg.threadId)
Promise.props(threads).then (threads) =>
messages = _.filter messages, (msg) =>
threads[msg.threadId]?
if messages.length > 0
@_onNewMailReceived({message: messages, thread: _.values(threads)})
, 10000
_onNewMailReceived: (incoming) ->
new Promise (resolve, reject) =>
return resolve() if atom.config.get('core.notifications.enabled') is false
incomingMessages = incoming['message'] ? []
incomingThreads = incoming['thread'] ? []
# Filter for new messages that are not sent by the current user
newUnread = _.filter incomingMessages, (msg) =>
isUnread = msg.unread is true
isNew = msg.date?.valueOf() >= @activationTime
isFromMe = msg.isFromMe()
return isUnread and isNew and not isFromMe
return resolve() if newUnread.length is 0
# For each message, find it's corresponding thread. First, look to see
# if it's already in the `incoming` payload (sent via delta sync
# at the same time as the message.) If it's not, try loading it from
# the local cache.
#
# Note we may receive multiple unread msgs for the same thread.
# Using a map and ?= to avoid repeating work.
threads = {}
for msg in newUnread
threads[msg.threadId] ?= _.findWhere(incomingThreads, {id: msg.threadId})
threads[msg.threadId] ?= DatabaseStore.find(Thread, msg.threadId)
Promise.props(threads).then (threads) =>
# Filter new unread messages to just the ones in the inbox
newUnreadInInbox = _.filter newUnread, (msg) ->
threads[msg.threadId]?.categoryNamed('inbox')
# Filter messages that we can't decide whether to display or not
# since no associated Thread object has arrived yet.
newUnreadMissingThreads = _.filter newUnread, (msg) ->
not threads[msg.threadId]?
if newUnreadMissingThreads.length > 0
@_onNewMessagesMissingThreads(newUnreadMissingThreads)
return resolve() if newUnreadInInbox.length is 0
if atom.config.get("core.notifications.sounds")
SoundRegistry.playSound('new-mail')
for msg in newUnreadInInbox
@stack.push({message: msg, thread: threads[msg.threadId]})
if not @stackProcessTimer
@_notifyMessages()
resolve()