From f038c97144a289ab1806a804065aeefe83fe81f4 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Sat, 24 Jun 2017 18:40:41 -0700 Subject: [PATCH] Move thread counts logic to c++ --- ...{Custom-Pro-Blond.otf => Custom-Blond.otf} | Bin .../{Custom-Pro-Hair.otf => Custom-Hair.otf} | Bin ...{Custom-Pro-Light.otf => Custom-Light.otf} | Bin ...ustom-Pro-Medium.otf => Custom-Medium.otf} | Bin ...ustom-Pro-Normal.otf => Custom-Normal.otf} | Bin ...m-Pro-SemiBold.otf => Custom-SemiBold.otf} | Bin .../{Custom-Pro-Thin.otf => Custom-Thin.otf} | Bin .../thread-list/lib/thread-list-columns.cjsx | 4 +- .../thread-list/lib/thread-list.cjsx | 8 +- .../spec/fixtures/db-test-model.coffee | 8 -- .../client-app/src/flux/models/category.es6 | 9 --- .../client-app/src/flux/models/contact.es6 | 10 --- packages/client-app/src/flux/models/event.es6 | 9 --- packages/client-app/src/flux/models/file.es6 | 10 +-- .../client-app/src/flux/models/message.es6 | 12 --- .../client-app/src/flux/models/thread.es6 | 34 +-------- .../flux/stores/thread-counts-store.coffee | 70 +----------------- 17 files changed, 13 insertions(+), 161 deletions(-) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Blond.otf => Custom-Blond.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Hair.otf => Custom-Hair.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Light.otf => Custom-Light.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Medium.otf => Custom-Medium.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Normal.otf => Custom-Normal.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-SemiBold.otf => Custom-SemiBold.otf} (100%) rename packages/client-app/internal_packages/custom-fonts/fonts/{Custom-Pro-Thin.otf => Custom-Thin.otf} (100%) diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Blond.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Blond.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Blond.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Blond.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Hair.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Hair.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Hair.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Hair.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Light.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Light.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Light.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Light.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Medium.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Medium.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Medium.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Medium.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Normal.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Normal.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Normal.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Normal.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-SemiBold.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-SemiBold.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-SemiBold.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-SemiBold.otf diff --git a/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Thin.otf b/packages/client-app/internal_packages/custom-fonts/fonts/Custom-Thin.otf similarity index 100% rename from packages/client-app/internal_packages/custom-fonts/fonts/Custom-Pro-Thin.otf rename to packages/client-app/internal_packages/custom-fonts/fonts/Custom-Thin.otf diff --git a/packages/client-app/internal_packages/thread-list/lib/thread-list-columns.cjsx b/packages/client-app/internal_packages/thread-list/lib/thread-list-columns.cjsx index 09a3735cd..5daf5be31 100644 --- a/packages/client-app/internal_packages/thread-list/lib/thread-list-columns.cjsx +++ b/packages/client-app/internal_packages/thread-list/lib/thread-list-columns.cjsx @@ -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 =
@@ -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 =
diff --git a/packages/client-app/internal_packages/thread-list/lib/thread-list.cjsx b/packages/client-app/internal_packages/thread-list/lib/thread-list.cjsx index 59291b6ca..a86ce4102 100644 --- a/packages/client-app/internal_packages/thread-list/lib/thread-list.cjsx +++ b/packages/client-app/internal_packages/thread-list/lib/thread-list.cjsx @@ -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' diff --git a/packages/client-app/spec/fixtures/db-test-model.coffee b/packages/client-app/spec/fixtures/db-test-model.coffee index 55d3de123..c7a56d590 100644 --- a/packages/client-app/spec/fixtures/db-test-model.coffee +++ b/packages/client-app/spec/fixtures/db-test-model.coffee @@ -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 diff --git a/packages/client-app/src/flux/models/category.es6 b/packages/client-app/src/flux/models/category.es6 index b2cb5f1f9..8ef5eed7e 100644 --- a/packages/client-app/src/flux/models/category.es6 +++ b/packages/client-app/src/flux/models/category.es6 @@ -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"); } diff --git a/packages/client-app/src/flux/models/contact.es6 b/packages/client-app/src/flux/models/contact.es6 index 9e67bd3e1..174590d56 100644 --- a/packages/client-app/src/flux/models/contact.es6 +++ b/packages/client-app/src/flux/models/contact.es6 @@ -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']; diff --git a/packages/client-app/src/flux/models/event.es6 b/packages/client-app/src/flux/models/event.es6 index e5c966870..be9b9bcbd 100644 --- a/packages/client-app/src/flux/models/event.es6 +++ b/packages/client-app/src/flux/models/event.es6 @@ -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'] diff --git a/packages/client-app/src/flux/models/file.es6 b/packages/client-app/src/flux/models/file.es6 index 79e819af2..ebe24658e 100644 --- a/packages/client-app/src/flux/models/file.es6 +++ b/packages/client-app/src/flux/models/file.es6 @@ -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', }), }); diff --git a/packages/client-app/src/flux/models/message.es6 b/packages/client-app/src/flux/models/message.es6 index 78f5335a2..07716ef7c 100644 --- a/packages/client-app/src/flux/models/message.es6 +++ b/packages/client-app/src/flux/models/message.es6 @@ -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 || "" diff --git a/packages/client-app/src/flux/models/thread.es6 b/packages/client-app/src/flux/models/thread.es6 index 12d3bd42a..8f84627c0 100644 --- a/packages/client-app/src/flux/models/thread.es6 +++ b/packages/client-app/src/flux/models/thread.es6 @@ -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'] diff --git a/packages/client-app/src/flux/stores/thread-counts-store.coffee b/packages/client-app/src/flux/stores/thread-counts-store.coffee index 6707a2590..749bb7c18 100644 --- a/packages/client-app/src/flux/stores/thread-counts-store.coffee +++ b/packages/client-app/src/flux/stores/thread-counts-store.coffee @@ -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