mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 07:46:06 +08:00
Move thread counts logic to c++
This commit is contained in:
parent
f37aa13780
commit
f038c97144
|
@ -94,7 +94,7 @@ c3 = new ListTabular.Column
|
|||
attachment = false
|
||||
messages = thread.__messages || []
|
||||
|
||||
hasAttachments = thread.hasAttachments and messages.find (m) -> Utils.showIconForAttachments(m.files)
|
||||
hasAttachments = thread.attachmentCount > 0 and messages.find (m) -> Utils.showIconForAttachments(m.files)
|
||||
if hasAttachments
|
||||
attachment = <div className="thread-icon thread-icon-attachment"></div>
|
||||
|
||||
|
@ -144,7 +144,7 @@ cNarrow = new ListTabular.Column
|
|||
attachment = false
|
||||
messages = thread.__messages || []
|
||||
|
||||
hasAttachments = thread.hasAttachments and messages.find (m) -> Utils.showIconForAttachments(m.files)
|
||||
hasAttachments = thread.attachmentCount > 0 and messages.find (m) -> Utils.showIconForAttachments(m.files)
|
||||
if hasAttachments
|
||||
attachment = <div className="thread-icon thread-icon-attachment"></div>
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ classnames = require 'classnames'
|
|||
CanvasUtils,
|
||||
TaskFactory,
|
||||
ChangeStarredTask,
|
||||
ChangeFolderTask,
|
||||
ChangeLabelsTask,
|
||||
WorkspaceStore,
|
||||
AccountStore,
|
||||
CategoryStore,
|
||||
|
@ -154,8 +156,10 @@ class ThreadList extends React.Component
|
|||
task = tasks[0]
|
||||
name = if task instanceof ChangeStarredTask
|
||||
'unstar'
|
||||
else if task.categoriesToAdd().length is 1
|
||||
task.categoriesToAdd()[0].name
|
||||
else if task instanceof ChangeFolderTask
|
||||
task.folder.name
|
||||
else if task instanceof ChangeLabelsTask and task.labelsToAdd.length is 1
|
||||
task.labelsToAdd[0].name
|
||||
else
|
||||
'remove'
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ class TestModel extends Model
|
|||
jsonKey: 'server_id'
|
||||
|
||||
TestModel.configureBasic = ->
|
||||
TestModel.additionalSQLiteConfig = undefined
|
||||
TestModel.attributes =
|
||||
'id': Attributes.String
|
||||
queryable: true
|
||||
|
@ -34,7 +33,6 @@ TestModel.configureBasic = ->
|
|||
jsonKey: 'server_id'
|
||||
|
||||
TestModel.configureWithAllAttributes = ->
|
||||
TestModel.additionalSQLiteConfig = undefined
|
||||
TestModel.attributes =
|
||||
'datetime': Attributes.DateTime
|
||||
queryable: true
|
||||
|
@ -53,7 +51,6 @@ TestModel.configureWithAllAttributes = ->
|
|||
modelKey: 'other'
|
||||
|
||||
TestModel.configureWithCollectionAttribute = ->
|
||||
TestModel.additionalSQLiteConfig = undefined
|
||||
TestModel.attributes =
|
||||
'id': Attributes.String
|
||||
queryable: true
|
||||
|
@ -77,7 +74,6 @@ TestModel.configureWithCollectionAttribute = ->
|
|||
joinQueryableBy: ['other'],
|
||||
|
||||
TestModel.configureWithJoinedDataAttribute = ->
|
||||
TestModel.additionalSQLiteConfig = undefined
|
||||
TestModel.attributes =
|
||||
'id': Attributes.String
|
||||
queryable: true
|
||||
|
@ -95,7 +91,6 @@ TestModel.configureWithJoinedDataAttribute = ->
|
|||
modelKey: 'body'
|
||||
|
||||
|
||||
TestModel.configureWithAdditionalSQLiteConfig = ->
|
||||
TestModel.attributes =
|
||||
'id': Attributes.String
|
||||
queryable: true
|
||||
|
@ -109,8 +104,5 @@ TestModel.configureWithAdditionalSQLiteConfig = ->
|
|||
'body': Attributes.JoinedData
|
||||
modelTable: 'TestModelBody'
|
||||
modelKey: 'body'
|
||||
TestModel.additionalSQLiteConfig =
|
||||
setup: ->
|
||||
['CREATE INDEX IF NOT EXISTS ThreadListIndex ON Thread(last_message_received_timestamp DESC, account_id, id)']
|
||||
|
||||
module.exports = TestModel
|
||||
|
|
|
@ -118,15 +118,6 @@ export default class Category extends Model {
|
|||
return name;
|
||||
}
|
||||
|
||||
static additionalSQLiteConfig = {
|
||||
setup: () => {
|
||||
return [
|
||||
// 'CREATE INDEX IF NOT EXISTS FolderNameIndex ON Folder(accountId,name)',
|
||||
// 'CREATE UNIQUE INDEX IF NOT EXISTS FolderClientIndex ON Folder(id)',
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
displayType() {
|
||||
throw new Error("Base class");
|
||||
}
|
||||
|
|
|
@ -90,16 +90,6 @@ export default class Contact extends Model {
|
|||
}),
|
||||
});
|
||||
|
||||
static additionalSQLiteConfig = {
|
||||
setup: () => {
|
||||
return [
|
||||
'CREATE INDEX IF NOT EXISTS ContactEmailIndex ON Contact(email)',
|
||||
'CREATE INDEX IF NOT EXISTS ContactAccountEmailIndex ON Contact(accountId, email)',
|
||||
'CREATE INDEX IF NOT EXISTS ContactIsSearchIndexedIndex ON `Contact` (isSearchIndexed, id)',
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
static searchable = true;
|
||||
|
||||
static searchFields = ['content'];
|
||||
|
|
|
@ -127,15 +127,6 @@ export default class Event extends Model {
|
|||
}),
|
||||
});
|
||||
|
||||
static additionalSQLiteConfig = {
|
||||
setup: () => {
|
||||
return [
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS EventClientIndex ON Event(id)',
|
||||
'CREATE INDEX IF NOT EXISTS EventIsSearchIndexedIndex ON `Event` (is_search_indexed, id)',
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
static searchable = true
|
||||
|
||||
static searchFields = ['title', 'description', 'location', 'participants']
|
||||
|
|
|
@ -29,25 +29,19 @@ export default class File extends Model {
|
|||
static attributes = Object.assign({}, Model.attributes, {
|
||||
filename: Attributes.String({
|
||||
modelKey: 'filename',
|
||||
jsonKey: 'filename',
|
||||
queryable: true,
|
||||
}),
|
||||
size: Attributes.Number({
|
||||
modelKey: 'size',
|
||||
jsonKey: 'size',
|
||||
}),
|
||||
contentType: Attributes.String({
|
||||
modelKey: 'contentType',
|
||||
jsonKey: 'content_type',
|
||||
}),
|
||||
messageIds: Attributes.Collection({
|
||||
modelKey: 'messageIds',
|
||||
jsonKey: 'message_ids',
|
||||
itemClass: String,
|
||||
messageId: Attributes.ServerId({
|
||||
modelKey: 'messageId',
|
||||
}),
|
||||
contentId: Attributes.String({
|
||||
modelKey: 'contentId',
|
||||
jsonKey: 'content_id',
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
@ -181,18 +181,6 @@ export default class Message extends ModelWithMetadata {
|
|||
return Message.attributes.date.ascending()
|
||||
}
|
||||
|
||||
static additionalSQLiteConfig = {
|
||||
setup: () => [
|
||||
`CREATE INDEX IF NOT EXISTS MessageListThreadIndex ON Message(threadId, date ASC)`,
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS MessageDraftIndex ON Message(id)`,
|
||||
`CREATE INDEX IF NOT EXISTS MessageListDraftIndex ON \
|
||||
Message(accountId, date DESC) WHERE draft = 1`,
|
||||
`CREATE INDEX IF NOT EXISTS MessageListUnifiedDraftIndex ON \
|
||||
Message(date DESC) WHERE draft = 1`,
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS MessageBodyIndex ON MessageBody(id)`,
|
||||
],
|
||||
}
|
||||
|
||||
constructor(args) {
|
||||
super(args);
|
||||
this.subject = this.subject || ""
|
||||
|
|
|
@ -94,8 +94,8 @@ class Thread extends ModelWithMetadata {
|
|||
itemClass: Contact,
|
||||
}),
|
||||
|
||||
hasAttachments: Attributes.Boolean({
|
||||
modelKey: 'hasAttachments',
|
||||
attachmentCount: Attributes.Number({
|
||||
modelKey: 'attachmentCount',
|
||||
}),
|
||||
|
||||
lastMessageReceivedTimestamp: Attributes.DateTime({
|
||||
|
@ -136,36 +136,6 @@ class Thread extends ModelWithMetadata {
|
|||
return Thread.sortOrderAttribute().descending()
|
||||
}
|
||||
|
||||
static additionalSQLiteConfig = {
|
||||
setup: () => [
|
||||
// ThreadCounts
|
||||
'CREATE TABLE IF NOT EXISTS `ThreadCounts` (`category_id` TEXT PRIMARY KEY, `unread` INTEGER, `total` INTEGER)',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS ThreadCountsIndex ON `ThreadCounts` (category_id DESC)',
|
||||
|
||||
// ThreadContact
|
||||
'CREATE INDEX IF NOT EXISTS ThreadContactDateIndex ON `ThreadContact` (lastMessageReceivedTimestamp DESC, value, id)',
|
||||
|
||||
// ThreadCategory
|
||||
'CREATE INDEX IF NOT EXISTS ThreadListCategoryIndex ON `ThreadCategory` (lastMessageReceivedTimestamp DESC, value, inAllMail, unread, id)',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadListCategorySentIndex ON `ThreadCategory` (lastMessageSentTimestamp DESC, value, inAllMail, unread, id)',
|
||||
|
||||
// Thread: General index
|
||||
'CREATE INDEX IF NOT EXISTS ThreadDateIndex ON `Thread` (lastMessageReceivedTimestamp DESC)',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadClientIdIndex ON `Thread` (id)',
|
||||
|
||||
// Thread: Partial indexes for specific views
|
||||
'CREATE INDEX IF NOT EXISTS ThreadUnreadIndex ON `Thread` (accountId, lastMessageReceivedTimestamp DESC) WHERE unread = 1 AND inAllMail = 1',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadUnifiedUnreadIndex ON `Thread` (lastMessageReceivedTimestamp DESC) WHERE unread = 1 AND inAllMail = 1',
|
||||
|
||||
'DROP INDEX IF EXISTS `Thread`.ThreadStarIndex',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadStarredIndex ON `Thread` (accountId, lastMessageReceivedTimestamp DESC) WHERE starred = 1 AND inAllMail = 1',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadUnifiedStarredIndex ON `Thread` (lastMessageReceivedTimestamp DESC) WHERE starred = 1 AND inAllMail = 1',
|
||||
|
||||
'CREATE INDEX IF NOT EXISTS ThreadIsSearchIndexedIndex ON `Thread` (isSearchIndexed, id)',
|
||||
'CREATE INDEX IF NOT EXISTS ThreadIsSearchIndexedLastMessageReceivedIndex ON `Thread` (isSearchIndexed, lastMessageReceivedTimestamp)',
|
||||
],
|
||||
}
|
||||
|
||||
static searchable = true
|
||||
|
||||
static searchFields = ['subject', 'to_', 'from_', 'categories', 'body']
|
||||
|
|
|
@ -3,65 +3,9 @@ NylasStore = require 'nylas-store'
|
|||
DatabaseStore = require('./database-store').default
|
||||
Thread = require('../models/thread').default
|
||||
|
||||
###
|
||||
Are running two nested SELECT statements really the best option? Yup.
|
||||
For a performance assessment of these queries and other options, see:
|
||||
https://gist.github.com/bengotow/c8b5cd8989c9149ded56
|
||||
|
||||
Note: SUM(unread) works because unread is represented as an int: 0 or 1.
|
||||
###
|
||||
|
||||
ReadCountsQuery = ->
|
||||
"SELECT * FROM `ThreadCounts`"
|
||||
|
||||
SetCountsQuery = ->
|
||||
"""
|
||||
REPLACE INTO `ThreadCounts` (categoryId, unread, total)
|
||||
SELECT
|
||||
`ThreadCategory`.`value` as categoryId,
|
||||
SUM(`ThreadCategory`.`unread`) as unread,
|
||||
COUNT(*) as total
|
||||
FROM `ThreadCategory`
|
||||
WHERE
|
||||
`ThreadCategory`.inAllMail = 1
|
||||
GROUP BY `ThreadCategory`.`value`;
|
||||
"""
|
||||
|
||||
UpdateCountsQuery = (objectIds, operator) ->
|
||||
objectIdsString = "'" + objectIds.join("','") + "'"
|
||||
"""
|
||||
REPLACE INTO `ThreadCounts` (categoryId, unread, total)
|
||||
SELECT
|
||||
`ThreadCategory`.`value` as categoryId,
|
||||
COALESCE((SELECT unread FROM `ThreadCounts` WHERE categoryId = `ThreadCategory`.`value`), 0) #{operator} SUM(`ThreadCategory`.`unread`) as unread,
|
||||
COALESCE((SELECT total FROM `ThreadCounts` WHERE categoryId = `ThreadCategory`.`value`), 0) #{operator} COUNT(*) as total
|
||||
FROM `ThreadCategory`
|
||||
WHERE
|
||||
`ThreadCategory`.id IN (#{objectIdsString}) AND
|
||||
`ThreadCategory`.inAllMail = 1
|
||||
GROUP BY `ThreadCategory`.`value`
|
||||
"""
|
||||
|
||||
class CategoryDatabaseMutationObserver
|
||||
beforeDatabaseChange: (query, {type, objects, objectIds, objectClass}) =>
|
||||
if objectClass is Thread.name
|
||||
query(UpdateCountsQuery(objectIds, '-'))
|
||||
else
|
||||
Promise.resolve()
|
||||
|
||||
afterDatabaseChange: (query, {type, objects, objectIds, objectClass}, beforeResolveValue) =>
|
||||
if objectClass is Thread.name
|
||||
query(UpdateCountsQuery(objectIds, '+'))
|
||||
else
|
||||
Promise.resolve()
|
||||
|
||||
class ThreadCountsStore extends NylasStore
|
||||
CategoryDatabaseMutationObserver: CategoryDatabaseMutationObserver
|
||||
|
||||
constructor: ->
|
||||
@_counts = {}
|
||||
@_observer = new CategoryDatabaseMutationObserver()
|
||||
DatabaseStore.addMutationHook(@_observer)
|
||||
|
||||
if NylasEnv.isMainWindow()
|
||||
# For now, unread counts are only retrieved in the main window.
|
||||
|
@ -71,20 +15,8 @@ class ThreadCountsStore extends NylasStore
|
|||
@_onCountsChangedDebounced()
|
||||
@_onCountsChangedDebounced()
|
||||
|
||||
if NylasEnv.isWorkWindow() and not NylasEnv.config.get('nylas.threadCountsValid')
|
||||
@reset()
|
||||
|
||||
reset: =>
|
||||
countsStartTime = null
|
||||
DatabaseStore.inTransaction (t) =>
|
||||
countsStartTime = Date.now()
|
||||
DatabaseStore._query(SetCountsQuery())
|
||||
.then =>
|
||||
NylasEnv.config.set('nylas.threadCountsValid', true)
|
||||
console.log("Recomputed all thread counts in #{Date.now() - countsStartTime}ms")
|
||||
|
||||
_onCountsChanged: =>
|
||||
DatabaseStore._query(ReadCountsQuery()).then (results) =>
|
||||
DatabaseStore._query("SELECT * FROM `ThreadCounts`").then (results) =>
|
||||
nextCounts = {}
|
||||
|
||||
foundNegative = false
|
||||
|
|
Loading…
Reference in a new issue