fix(unread): counts should exclude items outside all mail

#1726, #1718
This commit is contained in:
Ben Gotow 2016-03-14 18:32:59 -07:00
parent e5c2075d26
commit 77b86dd087
2 changed files with 140 additions and 62 deletions

View file

@ -7,6 +7,42 @@ Category = require '../../src/flux/models/category'
Matcher = require '../../src/flux/attributes/matcher'
WindowBridge = require '../../src/window-bridge'
category1 = new Category(id: "l1", name: "inbox", displayName: "Inbox")
category2 = new Category(id: "l2", name: "archive", displayName: "Archive")
category3 = new Category(id: "l3", displayName: "Happy Days")
category4 = new Category(id: "l4", displayName: "Sad Days")
category5 = new Category(id: "l5", name: 'all', displayName: "All Mail")
category6 = new Category(id: "l6", name: 'trash', displayName: "Trash")
# Values here are the "after" state. Below, the spy on the query returns the
# "current" state.
threadA = new Thread
id: "A"
unread: true
categories: [category1, category4, category5]
categoriesType: 'labels'
threadB = new Thread
id: "B"
unread: true
categories: [category3, category5]
categoriesType: 'labels'
threadC = new Thread
id: "C"
unread: false
categories: [category1, category3, category5]
categoriesType: 'labels'
threadD = new Thread
id: "D"
unread: true
categories: [category6]
categoriesType: 'labels'
threadE = new Thread
id: "E"
unread: true
categories: [category1, category5]
categoriesType: 'labels'
describe "ThreadCountsStore", ->
describe "unreadCountForCategoryId", ->
it "returns null if no count exists for the category id", ->
@ -177,80 +213,121 @@ describe "ThreadCountsStore", ->
describe "CategoryDatabaseMutationObserver", ->
beforeEach ->
@category1 = new Category(id: "l1", name: "inbox", displayName: "Inbox")
@category2 = new Category(id: "l2", name: "archive", displayName: "Archive")
@category3 = new Category(id: "l3", displayName: "Happy Days")
@category4 = new Category(id: "l4", displayName: "Sad Days")
@queryResolves = []
@query = jasmine.createSpy('query').andCallFake =>
new Promise (resolve, reject) =>
@queryResolves.push(resolve)
# Values here are the "after" state. Below, the spy on the query returns the
# "current" state.
@threadA = new Thread
id: "A"
unread: true
categories: [@category1, @category4]
@threadB = new Thread
id: "B"
unread: true
categories: [@category3]
@threadC = new Thread
id: "C"
unread: false
categories: [@category1, @category3]
@countsDidChange = jasmine.createSpy('countsDidChange')
@m = new ThreadCountsStore.CategoryDatabaseMutationObserver(@countsDidChange)
describe "given a set of modifying models", ->
scenarios = [{
scenarios = [
{
name: 'Persisting a three threads, two unread, all in all mail'
type: 'persist',
expected: {
l3: -1,
l2: -1,
l4: 1
}
},{
type: 'unpersist',
expected: {
threads: [threadA, threadB, threadC],
beforePersistQueryResults: [
{id: threadA.id, catId: category1.id},
{id: threadA.id, catId: category3.id},
{id: threadA.id, catId: category5.id},
{id: threadB.id, catId: category2.id},
{id: threadB.id, catId: category5.id},
{id: threadB.id, catId: category3.id},
{id: threadC.id, catId: category5.id},
]
beforePersistExpected: {
l1: -1,
l3: -2,
l2: -1,
l5: -3
}
afterPersistExpected: {
l3: -1,
l5: -1,
l2: -1,
l4: 1,
}
},
{
name: 'Unpersisting a normal set of threads, all in all mail'
type: 'unpersist',
threads: [threadA, threadB, threadC],
beforePersistQueryResults: [
{id: threadA.id, catId: category1.id},
{id: threadA.id, catId: category3.id},
{id: threadA.id, catId: category5.id},
{id: threadB.id, catId: category2.id},
{id: threadB.id, catId: category5.id},
{id: threadB.id, catId: category3.id},
{id: threadC.id, catId: category5.id},
]
beforePersistExpected: {
l1: -1,
l3: -2,
l2: -1,
l5: -3
}
afterPersistExpected: {
l1: -1,
l5: -3,
l3: -2,
l2: -1
}
}]
scenarios.forEach ({type, expected}) ->
it "should call countsDidChange with the category membership deltas (#{type})", ->
queryResolves = []
query = jasmine.createSpy('query').andCallFake =>
new Promise (resolve, reject) ->
queryResolves.push(resolve)
countsDidChange = jasmine.createSpy('countsDidChange')
m = new ThreadCountsStore.CategoryDatabaseMutationObserver(countsDidChange)
beforePromise = m.beforeDatabaseChange(query, {
},
{
name: 'Thread D going from inbox to trash'
type: 'persist',
threads: [threadD],
beforePersistQueryResults: [
{id: threadD.id, catId: category1.id},
{id: threadD.id, catId: category3.id},
{id: threadD.id, catId: category4.id},
]
beforePersistExpected: {
l1: -1,
l3: -1,
l4: -1
}
afterPersistExpected: {
l1: -1,
l3: -1,
l4: -1,
}
},
{
name: 'Thread E going from trash to inbox'
type: 'persist',
threads: [threadE],
beforePersistQueryResults: [
]
beforePersistExpected: {
}
afterPersistExpected: {
l1: 1,
l5: 1
}
},
]
scenarios.forEach ({name, type, threads, beforePersistQueryResults, beforePersistExpected, afterPersistExpected}) ->
it "should call countsDidChange with the category membership deltas (#{name})", ->
beforePromise = @m.beforeDatabaseChange(@query, {
type: type
objects: [@threadA, @threadB, @threadC],
objectIds: [@threadA.id, @threadB.id, @threadC.id]
objects: threads,
objectIds: _.pluck(threads, 'id'),
objectClass: Thread.name
})
expect(query.callCount).toBe(1)
expect(query.calls[0].args[0]).toEqual("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 ('A','B','C') AND `Thread`.unread = 1")
queryResolves[0]([
{id: @threadA.id, catId: @category1.id},
{id: @threadA.id, catId: @category3.id},
{id: @threadB.id, catId: @category2.id},
{id: @threadB.id, catId: @category3.id},
])
expect(@query.callCount).toBe(1)
expect(@query.calls[0].args[0]).toEqual("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 ('#{_.pluck(threads, 'id').join("','")}') AND `Thread`.unread = 1 AND `Thread`.in_all_mail = 1")
@queryResolves[0](beforePersistQueryResults)
waitsForPromise =>
beforePromise.then (result) =>
expect(result).toEqual({
categories: {
l1: -1,
l3: -2,
l2: -1
}
})
m.afterDatabaseChange(query, {
expect(result).toEqual({categories: beforePersistExpected})
@m.afterDatabaseChange(@query, {
type: type
objects: [@threadA, @threadB, @threadC],
objectIds: [@threadA.id, @threadB.id, @threadC.id]
objects: threads,
objectIds: _.pluck(threads, 'id'),
objectClass: Thread.name
}, result)
expect(countsDidChange).toHaveBeenCalledWith(expected)
expect(@countsDidChange).toHaveBeenCalledWith(afterPersistExpected)

View file

@ -10,7 +10,7 @@ Thread = require '../models/thread'
Category = require '../models/category'
WindowBridge = require '../../window-bridge'
JSONBlobKey = 'UnreadCounts-V2'
JSONBlobKey = 'UnreadCounts-V3'
class CategoryDatabaseMutationObserver
constructor: (@_countsDidChange) ->
@ -18,7 +18,7 @@ class CategoryDatabaseMutationObserver
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", [])
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 AND `Thread`.in_all_mail = 1", [])
.then (categoryData) =>
categories = {}
for {id, catId} in categoryData
@ -35,6 +35,7 @@ class CategoryDatabaseMutationObserver
if type is 'persist'
for thread in objects
continue unless thread.unread
continue unless thread.inAllMail
for cat in thread.categories
categories[cat.id] ?= 0
categories[cat.id] += 1