fix(trash/spam): New inAllMail filter applied to all mailbox views except trash and spam

This commit is contained in:
Ben Gotow 2016-03-10 12:20:03 -08:00
parent b920cf83df
commit 96c0b5da29
6 changed files with 101 additions and 13 deletions

View file

@ -20,6 +20,38 @@ describe 'Thread', ->
iterations += 1
console.log((Date.now() - start) / 1000.0 + "ms per 1000")
describe "inAllMail", ->
describe "when the thread categoriesType is 'folders'", ->
it "should return true", ->
thread = new Thread(categoriesType: 'folders', categories: [new Category(name: 'inbox')])
expect(thread.inAllMail).toBe(true)
# Unlike Gmail, this means half the thread is in trash and half is in sent.
# It should still appear in results for "Sent"
thread = new Thread(categoriesType: 'folders', categories: [new Category(name: 'sent'), new Category(name: 'trash')])
expect(thread.inAllMail).toBe(true)
describe "when the thread categoriesType is 'labels'", ->
it "should return true if the thread has an all category", ->
thread = new Thread(categoriesType: 'labels', categories: [new Category(name: 'all')])
expect(thread.inAllMail).toBe(true)
# thread is half in spam
thread = new Thread(categoriesType: 'labels', categories: [new Category(name: 'all'), new Category(name: 'inbox'), new Category(name: 'spam')])
expect(thread.inAllMail).toBe(true)
it "should return false if the thread has the spam category and no all mail", ->
thread = new Thread(categoriesType: 'labels', categories: [new Category(name: 'sent'), new Category(name: 'spam')])
expect(thread.inAllMail).toBe(false)
it "should return false if the thread has the trash category and no all mail", ->
thread = new Thread(categoriesType: 'labels', categories: [new Category(name: 'sent'), new Category(name: 'trash')])
expect(thread.inAllMail).toBe(false)
it "should return true if the thread has none of the above (assume all mail)", ->
thread = new Thread(categoriesType: 'labels', categories: [new Category(name: 'inbox')])
expect(thread.inAllMail).toBe(true)
describe 'sortedCategories', ->
sortedForCategoryNames = (inputs) ->
categories = _.map inputs, (i) ->

View file

@ -30,7 +30,7 @@ describe 'Utils', ->
subject: 'Test 1234'
it "should serialize and de-serialize models correctly", ->
expectedString = '[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"id":"local-b"}],"id":"local-1","__constructorName":"Thread"}]'
expectedString = '[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"id":"local-b"}],"in_all_mail":true,"id":"local-1","__constructorName":"Thread"}]'
jsonString = JSON.stringify([@testThread], Utils.registeredObjectReplacer)
expect(jsonString).toEqual(expectedString)
@ -40,7 +40,7 @@ describe 'Utils', ->
it "should re-inflate Models in places they're not explicitly declared types", ->
b = new JSONBlob({id: "local-ThreadsToProcess", json: [@testThread]})
jsonString = JSON.stringify(b, Utils.registeredObjectReplacer)
expectedString = '{"client_id":"local-ThreadsToProcess","server_id":"local-ThreadsToProcess","json":[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"id":"local-b"}],"id":"local-1","__constructorName":"Thread"}],"id":"local-ThreadsToProcess","__constructorName":"JSONBlob"}'
expectedString = '{"client_id":"local-ThreadsToProcess","server_id":"local-ThreadsToProcess","json":[{"client_id":"local-1","account_id":"1","metadata":[],"subject":"Test 1234","participants":[{"client_id":"local-a","account_id":"1","name":"Juan","email":"juan@nylas.com","thirdPartyData":{},"id":"local-a"},{"client_id":"local-b","account_id":"1","name":"Ben","email":"ben@nylas.com","thirdPartyData":{},"id":"local-b"}],"in_all_mail":true,"id":"local-1","__constructorName":"Thread"}],"id":"local-ThreadsToProcess","__constructorName":"JSONBlob"}'
expect(jsonString).toEqual(expectedString)
revived = JSON.parse(jsonString, Utils.registeredObjectReviver)

View file

@ -151,7 +151,34 @@ class OrCompositeMatcher extends Matcher
wheres.push(matcher.whereSQL(klass))
return "(" + wheres.join(" OR ") + ")"
class AndCompositeMatcher extends Matcher
constructor: (@children) ->
@
attribute: =>
null
value: =>
null
evaluate: (model) =>
_.every @children, (matcher) -> matcher.evaluate(model)
joinSQL: (klass) =>
joins = []
for matcher in @children
join = matcher.joinSQL(klass)
joins.push(join) if join
return joins
whereSQL: (klass) =>
wheres = []
for matcher in @children
wheres.push(matcher.whereSQL(klass))
return "(" + wheres.join(" AND ") + ")"
Matcher.muid = 0
Matcher.Or = OrCompositeMatcher
Matcher.And = AndCompositeMatcher
module.exports = Matcher

View file

@ -65,6 +65,9 @@ class Thread extends ModelWithMetadata
modelKey: 'categories'
itemClass: Category
'categoriesType': Attributes.String
modelKey: 'categoriesType'
'participants': Attributes.Collection
queryable: true
joinOnField: 'email'
@ -84,6 +87,19 @@ class Thread extends ModelWithMetadata
modelKey: 'lastMessageSentTimestamp'
jsonKey: 'last_message_sent_timestamp'
'inAllMail': Attributes.Boolean
queryable: true
modelKey: 'inAllMail'
jsonKey: 'in_all_mail'
Object.defineProperty @attributes, "labels",
enumerable: false
get: -> @categories
Object.defineProperty @attributes, "folders",
enumerable: false
get: -> @categories
Object.defineProperty @prototype, "labels",
enumerable: false
get: -> @categories
@ -94,13 +110,17 @@ class Thread extends ModelWithMetadata
get: -> @categories
set: (v) -> @categories = v
Object.defineProperty @attributes, "labels",
Object.defineProperty @prototype, "inAllMail",
enumerable: false
get: -> @categories
Object.defineProperty @attributes, "folders",
enumerable: false
get: -> @categories
get: ->
if @categoriesType is 'labels'
inAllMail = _.any @categories, (cat) -> cat.name is 'all'
return true if inAllMail
inTrashOrSpam = _.any @categories, (cat) -> cat.name is 'trash' or cat.name is 'spam'
return true if not inTrashOrSpam
return false
else
return true
@naturalSortOrder: ->
Thread.attributes.lastMessageReceivedTimestamp.descending()
@ -108,14 +128,19 @@ class Thread extends ModelWithMetadata
@additionalSQLiteConfig:
setup: ->
['CREATE INDEX IF NOT EXISTS ThreadListIndex ON Thread(last_message_received_timestamp DESC, id)',
'CREATE INDEX IF NOT EXISTS ThreadListSentIndex ON Thread(last_message_sent_timestamp DESC, id)',
'CREATE INDEX IF NOT EXISTS ThreadStarIndex ON Thread(account_id, starred)']
fromJSON: (json) ->
super(json)
value = json['labels'] ? json['folders']
if value
@categories = @constructor.attributes.categories.fromJSON(value)
if json['folders']
@categoriesType = 'folders'
@categories = @constructor.attributes.categories.fromJSON(json['folders'])
if json['labels']
@categoriesType = 'labels'
@categories = @constructor.attributes.categories.fromJSON(json['labels'])
for attr in ['participants', 'categories']
value = @[attr]

View file

@ -16,7 +16,7 @@ DatabaseTransaction = require './database-transaction'
{ipcRenderer} = require 'electron'
DatabaseVersion = 20
DatabaseVersion = 21
DatabasePhase =
Setup: 'setup'
Ready: 'ready'

View file

@ -213,7 +213,8 @@ class StarredMailboxPerspective extends MailboxPerspective
threads: =>
query = DatabaseStore.findAll(Thread).where([
Thread.attributes.accountId.in(@accountIds),
Thread.attributes.starred.equal(true)
Thread.attributes.starred.equal(true),
Thread.attributes.inAllMail.equal(true),
]).limit(0)
return new MutableQuerySubscription(query, {asResultSet: true})
@ -275,6 +276,9 @@ class CategoryMailboxPerspective extends MailboxPerspective
if @isSent()
query.order(Thread.attributes.lastMessageSentTimestamp.descending())
unless @categoriesSharedName() in ['sent', 'trash']
query.where(inAllMail: true)
if @_categories.length > 1 and @accountIds.length < @_categories.length
# The user has multiple categories in the same account selected, which
# means our result set could contain multiple copies of the same threads