mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-12-09 14:05:53 +08:00
137 lines
4.5 KiB
CoffeeScript
137 lines
4.5 KiB
CoffeeScript
Reflux = require 'reflux'
|
|
Rx = require 'rx-lite'
|
|
_ = require 'underscore'
|
|
NylasStore = require 'nylas-store'
|
|
CategoryStore = require './category-store'
|
|
AccountStore = require './account-store'
|
|
DatabaseStore = require './database-store'
|
|
Actions = require '../actions'
|
|
Thread = require '../models/thread'
|
|
Category = require '../models/category'
|
|
WindowBridge = require '../../window-bridge'
|
|
|
|
JSONBlobKey = 'UnreadCounts-V2'
|
|
|
|
class CategoryDatabaseMutationObserver
|
|
constructor: (@_countsDidChange) ->
|
|
|
|
beforeDatabaseChange: (query, {type, objects, objectIds, objectClass}) =>
|
|
if objectClass is Thread.name
|
|
idString = "'" + objectIds.join("','") + "'"
|
|
query("SELECT `Thread`.id as id, `Thread-Category`.`value` as catId FROM `Thread` INNER JOIN `Thread-Category` ON `Thread`.`id` = `Thread-Category`.`id` WHERE `Thread`.id IN (#{idString}) AND `Thread`.unread = 1", [])
|
|
.then (categoryData) =>
|
|
categories = {}
|
|
for {id, catId} in categoryData
|
|
categories[catId] ?= 0
|
|
categories[catId] -= 1
|
|
Promise.resolve({categories})
|
|
else
|
|
Promise.resolve()
|
|
|
|
afterDatabaseChange: (query, {type, objects, objectIds, objectClass}, beforeResolveValue) =>
|
|
if objectClass is Thread.name
|
|
{categories} = beforeResolveValue
|
|
|
|
if type is 'persist'
|
|
for thread in objects
|
|
continue unless thread.unread
|
|
for cat in thread.categories
|
|
categories[cat.id] ?= 0
|
|
categories[cat.id] += 1
|
|
|
|
for key, val of categories
|
|
delete categories[key] if val is 0
|
|
|
|
if Object.keys(categories).length > 0
|
|
@_countsDidChange(categories)
|
|
|
|
Promise.resolve()
|
|
|
|
|
|
class ThreadCountsStore extends NylasStore
|
|
CategoryDatabaseMutationObserver: CategoryDatabaseMutationObserver
|
|
JSONBlobKey: JSONBlobKey
|
|
|
|
constructor: ->
|
|
@_counts = {}
|
|
@_deltas = {}
|
|
@_saveCountsSoon ?= _.throttle(@_saveCounts, 1000)
|
|
|
|
@_observer = new CategoryDatabaseMutationObserver(@_onCountsChanged)
|
|
DatabaseStore.addMutationHook(@_observer)
|
|
|
|
if NylasEnv.isWorkWindow()
|
|
DatabaseStore.findJSONBlob(JSONBlobKey).then(@_onCountsBlobRead)
|
|
Rx.Observable.fromQuery(DatabaseStore.findAll(Category)).subscribe (categories) =>
|
|
@_categories = [].concat(categories)
|
|
@_fetchCountsMissing()
|
|
|
|
else
|
|
query = DatabaseStore.findJSONBlob(JSONBlobKey)
|
|
Rx.Observable.fromQuery(query).subscribe(@_onCountsBlobRead)
|
|
|
|
unreadCountForCategoryId: (catId) =>
|
|
return null if @_counts[catId] is undefined
|
|
@_counts[catId] + (@_deltas[catId] || 0)
|
|
|
|
unreadCounts: =>
|
|
@_counts
|
|
|
|
_onCountsChanged: (metadata) =>
|
|
if not NylasEnv.isWorkWindow()
|
|
WindowBridge.runInWorkWindow("ThreadCountsStore", "_onCountsChanged", [metadata])
|
|
return
|
|
|
|
for catId, unread of metadata
|
|
@_deltas[catId] ?= 0
|
|
@_deltas[catId] += unread
|
|
@_saveCountsSoon()
|
|
|
|
_onCountsBlobRead: (json) =>
|
|
@_counts = json ? {}
|
|
@trigger()
|
|
|
|
# Fetch a count, populate it in the cache, and then call ourselves to
|
|
# populate the next missing count.
|
|
_fetchCountsMissing: =>
|
|
# Find a category missing a count
|
|
category = _.find @_categories, (cat) => !@_counts[cat.id]?
|
|
return unless category
|
|
|
|
# Reset the delta for the category, since we're about to fetch absolute count
|
|
@_deltas[category.id] = 0
|
|
|
|
@_fetchCountForCategory(category).then (unread) =>
|
|
# Only apply the count if we know it's still correct. If we've detected changes
|
|
# during the query, we can't know whether `unread` includes those or not.
|
|
# Just run the count query again in a few moments.
|
|
if @_deltas[category.id] is 0
|
|
@_counts[category.id] = unread
|
|
|
|
# We defer for a while - this means populating all the counts can take a while,
|
|
# but we don't want to flood the db with expensive SELECT COUNT queries.
|
|
_.delay(@_fetchCountsMissing, 3000)
|
|
@_saveCountsSoon()
|
|
|
|
# This method is not intended to return a promise and it
|
|
# could cause strange chaining.
|
|
return null
|
|
|
|
_saveCounts: =>
|
|
for key, count of @_deltas
|
|
continue if @_counts[key] is undefined
|
|
@_counts[key] += count
|
|
delete @_deltas[key]
|
|
|
|
DatabaseStore.inTransaction (t) =>
|
|
t.persistJSONBlob(JSONBlobKey, @_counts)
|
|
@trigger()
|
|
|
|
_fetchCountForCategory: (cat) =>
|
|
DatabaseStore.count(Thread, [
|
|
Thread.attributes.categories.contains(cat.id),
|
|
Thread.attributes.accountId.equal(cat.accountId),
|
|
Thread.attributes.unread.equal(true),
|
|
])
|
|
|
|
module.exports = new ThreadCountsStore
|