diff --git a/packages/client-app/src/flux/mailsync-bridge.es6 b/packages/client-app/src/flux/mailsync-bridge.es6 index ce9b35480..64b4cd4cf 100644 --- a/packages/client-app/src/flux/mailsync-bridge.es6 +++ b/packages/client-app/src/flux/mailsync-bridge.es6 @@ -72,8 +72,6 @@ export default class MailsyncBridge { } task.validate(); - - task.sequentialId = ++this._currentSequentialId; task.status = 'local'; this.sendMessageToAccount(task.accountId, {type: 'task-queued', task: task}); } diff --git a/packages/client-app/src/flux/models/category.es6 b/packages/client-app/src/flux/models/category.es6 index dfbb0bbc3..fde541e71 100644 --- a/packages/client-app/src/flux/models/category.es6 +++ b/packages/client-app/src/flux/models/category.es6 @@ -88,9 +88,6 @@ export default class Category extends Model { localStatus: Attributes.Object({ modelKey: 'localStatus', }), - _refcount: Attributes.Number({ - modelKey: '_refcount', - }), }); static Types = { diff --git a/packages/client-app/src/flux/models/message.es6 b/packages/client-app/src/flux/models/message.es6 index 538637ef1..5b6421ef8 100644 --- a/packages/client-app/src/flux/models/message.es6 +++ b/packages/client-app/src/flux/models/message.es6 @@ -168,10 +168,6 @@ export default class Message extends ModelWithMetadata { itemClass: Folder, }), - folderUID: Attributes.Number({ - modelKey: 'folderUID', - }), - }); static naturalSortOrder() { diff --git a/packages/client-app/src/flux/models/model.es6 b/packages/client-app/src/flux/models/model.es6 index 8d1dd28fc..4a35f0b93 100644 --- a/packages/client-app/src/flux/models/model.es6 +++ b/packages/client-app/src/flux/models/model.es6 @@ -40,7 +40,7 @@ export default class Model { this.fromJSON(data); } else { for (const key of Object.keys(this.constructor.attributes)) { - if (data[key]) { + if (data[key] !== undefined) { this[key] = data[key]; } } @@ -61,7 +61,7 @@ export default class Model { fromJSON(json) { for (const key of Object.keys(this.constructor.attributes)) { const attr = this.constructor.attributes[key]; - const attrValue = json[attr.jsonKey]; + const attrValue = json[attr.jsonKey || key]; if (attrValue !== undefined) { this[key] = attr.fromJSON(attrValue); } @@ -82,7 +82,7 @@ export default class Model { if (attrValue === undefined) { continue; } - json[attr.jsonKey] = attr.toJSON(attrValue); + json[attr.jsonKey || key] = attr.toJSON(attrValue); } json.__cls = this.constructor.name return json; diff --git a/packages/client-app/src/flux/stores/draft-factory.coffee b/packages/client-app/src/flux/stores/draft-factory.coffee index 61dbdcadf..1f392f280 100644 --- a/packages/client-app/src/flux/stores/draft-factory.coffee +++ b/packages/client-app/src/flux/stores/draft-factory.coffee @@ -30,7 +30,6 @@ class DraftFactory version: 0 unread: false starred: false - folderUID: 0 headerMessageId: Utils.generateTempId() + "@" + require('os').hostname() from: [account.defaultMe()] date: (new Date) diff --git a/packages/client-app/src/flux/tasks/change-folder-task.es6 b/packages/client-app/src/flux/tasks/change-folder-task.es6 index 304c650e4..c7db98040 100644 --- a/packages/client-app/src/flux/tasks/change-folder-task.es6 +++ b/packages/client-app/src/flux/tasks/change-folder-task.es6 @@ -1,5 +1,6 @@ -import Category from '../models/category'; import ChangeMailTask from './change-mail-task'; +import Attributes from '../attributes'; +import Folder from '../models/folder'; // Public: Create a new task to apply labels to a message or thread. // @@ -14,11 +15,15 @@ import ChangeMailTask from './change-mail-task'; // export default class ChangeFolderTask extends ChangeMailTask { - constructor(options = {}) { - super(options); - this.source = options.source - this.taskDescription = options.taskDescription; - this.folder = options.folder; + static attributes = Object.assign({}, ChangeMailTask.attributes, { + folder: Attributes.Object({ + modelKey: 'folder', + ItemClass: Folder, + }), + }); + + constructor(data = {}) { + super(data); } label() { @@ -33,10 +38,7 @@ export default class ChangeFolderTask extends ChangeMailTask { return this.taskDescription; } - let folderText = " to folder"; - if (this.folder instanceof Category) { - folderText = ` to ${this.folder.displayName}`; - } + const folderText = ` to ${this.folder.displayName}`; if (this.threadIds.length > 1) { return `Moved ${this.threadIds.length} threads${folderText}`; diff --git a/packages/client-app/src/flux/tasks/change-labels-task.es6 b/packages/client-app/src/flux/tasks/change-labels-task.es6 index f476b4d30..4afe016bc 100644 --- a/packages/client-app/src/flux/tasks/change-labels-task.es6 +++ b/packages/client-app/src/flux/tasks/change-labels-task.es6 @@ -1,6 +1,6 @@ import Label from '../models/label'; -import Category from '../models/category'; import ChangeMailTask from './change-mail-task'; +import Attributes from '../attributes'; // Public: Create a new task to apply labels to a message or thread. // @@ -9,14 +9,22 @@ import ChangeMailTask from './change-mail-task'; // - labelsToRemove: An {Array} of {Category}s or {Category} ids to remove // - threads: An {Array} of {Thread}s or {Thread} ids // - messages: An {Array} of {Message}s or {Message} ids +// export default class ChangeLabelsTask extends ChangeMailTask { + static attributes = Object.assign({}, ChangeMailTask.attributes, { + labelsToAdd: Attributes.Collection({ + modelKey: 'labelsToAdd', + ItemClass: Label, + }), + labelsToRemove: Attributes.Collection({ + modelKey: 'labelsToRemove', + ItemClass: Label, + }), + }); + constructor(options = {}) { super(options); - this.source = options.source - this.labelsToAdd = options.labelsToAdd || []; - this.labelsToRemove = options.labelsToRemove || []; - this.taskDescription = options.taskDescription; } label() { @@ -35,35 +43,29 @@ export default class ChangeLabelsTask extends ChangeMailTask { const removed = this.labelsToRemove[0]; const added = this.labelsToAdd[0]; - const objectsAvailable = (added || removed) instanceof Category; - // Note: In the future, we could move this logic to the task - // factory and pass the string in as this.taskDescription (ala Snooze), but - // it's nice to have them declaratively based on the actual labels. - if (objectsAvailable) { - // Spam / trash interactions are always "moves" because they're the three - // folders of Gmail. If another folder is involved, we need to decide to - // return either "Moved to Bla" or "Added Bla". - if (added && added.name === 'spam') { - return `Marked${countString} as Spam`; - } else if (removed && removed.name === 'spam') { - return `Unmarked${countString} as Spam`; - } else if (added && added.name === 'trash') { - return `Trashed${countString}`; - } else if (removed && removed.name === 'trash') { - return `Removed${countString} from Trash`; - } - if (this.labelsToAdd.length === 0 && this.labelsToRemove.find(l => l.role === 'inbox')) { - return `Archived${countString}`; - } else if (this.labelsToRemove.length === 0 && this.labelsToAdd.find(l => l.role === 'inbox')) { - return `Unarchived${countString}`; - } - if (this.labelsToAdd.length === 1 && this.labelsToRemove.length === 0) { - return `Added ${added.displayName}${countString ? ' to' : ''}${countString}`; - } - if (this.labelsToAdd.length === 0 && this.labelsToRemove.length === 1) { - return `Removed ${removed.displayName}${countString ? ' from' : ''}${countString}`; - } + // Spam / trash interactions are always "moves" because they're the three + // folders of Gmail. If another folder is involved, we need to decide to + // return either "Moved to Bla" or "Added Bla". + if (added && added.name === 'spam') { + return `Marked${countString} as Spam`; + } else if (removed && removed.name === 'spam') { + return `Unmarked${countString} as Spam`; + } else if (added && added.name === 'trash') { + return `Trashed${countString}`; + } else if (removed && removed.name === 'trash') { + return `Removed${countString} from Trash`; + } + if (this.labelsToAdd.length === 0 && this.labelsToRemove.find(l => l.role === 'inbox')) { + return `Archived${countString}`; + } else if (this.labelsToRemove.length === 0 && this.labelsToAdd.find(l => l.role === 'inbox')) { + return `Unarchived${countString}`; + } + if (this.labelsToAdd.length === 1 && this.labelsToRemove.length === 0) { + return `Added ${added.displayName}${countString ? ' to' : ''}${countString}`; + } + if (this.labelsToAdd.length === 0 && this.labelsToRemove.length === 1) { + return `Removed ${removed.displayName}${countString ? ' from' : ''}${countString}`; } return `Changed labels${countString ? ' on' : ''}${countString}`; } diff --git a/packages/client-app/src/flux/tasks/change-mail-task.es6 b/packages/client-app/src/flux/tasks/change-mail-task.es6 index 5915f07ed..57462edaf 100644 --- a/packages/client-app/src/flux/tasks/change-mail-task.es6 +++ b/packages/client-app/src/flux/tasks/change-mail-task.es6 @@ -1,4 +1,5 @@ import Task from './task'; +import Attributes from '../attributes'; /* Public: The ChangeMailTask is a base class for all tasks that modify sets @@ -20,8 +21,20 @@ requests. It does not call {ChangeMailTask::changesToModel}. */ export default class ChangeMailTask extends Task { - constructor({threads, thread, messages, message} = {}) { - super(); + static attributes = Object.assign({}, ChangeMailTask.attributes, { + taskDescription: Attributes.String({ + modelKey: 'taskDescription', + }), + threadIds: Attributes.Collection({ + modelKey: 'threadIds', + }), + messageIds: Attributes.Collection({ + modelKey: 'messageIds', + }), + }); + + constructor({threads, thread, messages, message, ...rest} = {}) { + super(rest); const t = threads || []; if (thread) { diff --git a/packages/client-app/src/flux/tasks/change-starred-task.es6 b/packages/client-app/src/flux/tasks/change-starred-task.es6 index 9fa3280b2..64305c938 100644 --- a/packages/client-app/src/flux/tasks/change-starred-task.es6 +++ b/packages/client-app/src/flux/tasks/change-starred-task.es6 @@ -1,15 +1,22 @@ /* eslint no-unused-vars: 0*/ import _ from 'underscore'; +import Attributes from '../attributes'; import Thread from '../models/thread'; import Actions from '../actions' import DatabaseStore from '../stores/database-store'; import ChangeMailTask from './change-mail-task'; export default class ChangeStarredTask extends ChangeMailTask { - constructor(options = {}) { - super(options); - this.source = options.source; - this.starred = options.starred; + + static attributes = Object.assign({}, ChangeMailTask.attributes, { + starred: Attributes.Boolean({ + modelKey: 'starred', + }), + }); + + constructor({starred, ...rest} = {}) { + super(rest); + this.starred = starred; } label() { diff --git a/packages/client-app/src/flux/tasks/change-unread-task.es6 b/packages/client-app/src/flux/tasks/change-unread-task.es6 index 6a4c69d2d..ee9207bd5 100644 --- a/packages/client-app/src/flux/tasks/change-unread-task.es6 +++ b/packages/client-app/src/flux/tasks/change-unread-task.es6 @@ -2,15 +2,25 @@ import _ from 'underscore'; import Thread from '../models/thread'; import Actions from '../actions' +import Attributes from '../attributes'; import DatabaseStore from '../stores/database-store'; import ChangeMailTask from './change-mail-task'; export default class ChangeUnreadTask extends ChangeMailTask { - constructor(options = {}) { - super(options); - this.source = options.source; - this.unread = options.unread; - this._canBeUndone = options.canBeUndone; + + static attributes = Object.assign({}, ChangeMailTask.attributes, { + starred: Attributes.Boolean({ + modelKey: 'unread', + }), + _canBeUndone: Attributes.Boolean({ + modelKey: '_canBeUndone', + }), + }); + + constructor({unread, canBeUndone, ...rest} = {}) { + super(rest); + this.unread = unread; + this._canBeUndone = canBeUndone; } label() { diff --git a/packages/client-app/src/flux/tasks/destroy-category-task.es6 b/packages/client-app/src/flux/tasks/destroy-category-task.es6 index 7665fe3e2..848713dd5 100644 --- a/packages/client-app/src/flux/tasks/destroy-category-task.es6 +++ b/packages/client-app/src/flux/tasks/destroy-category-task.es6 @@ -1,12 +1,13 @@ import Task from './task'; +import Attributes from '../attributes'; export default class DestroyCategoryTask extends Task { - constructor({path, accountId} = {}) { - super(); - this.path = path; - this.accountId = accountId; - } + static attributes = Object.assign({}, Task.attributes, { + path: Attributes.String({ + modelKey: 'path', + }), + }); label() { return `Deleting ${this.category.displayType()} ${this.category.displayName}` diff --git a/packages/client-app/src/flux/tasks/destroy-draft-task.es6 b/packages/client-app/src/flux/tasks/destroy-draft-task.es6 index b596f67b6..24543c79a 100644 --- a/packages/client-app/src/flux/tasks/destroy-draft-task.es6 +++ b/packages/client-app/src/flux/tasks/destroy-draft-task.es6 @@ -1,9 +1,11 @@ import Task from './task'; +import Attributes from '../attributes'; export default class DestroyDraftTask extends Task { - constructor(accountId, headerMessageId) { - super(); - this.accountId = accountId; - this.headerMessageId = headerMessageId; - } + + static attributes = Object.assign({}, Task.attributes, { + headerMessageId: Attributes.String({ + modelKey: 'headerMessageId', + }), + }); } diff --git a/packages/client-app/src/flux/tasks/send-draft-task.es6 b/packages/client-app/src/flux/tasks/send-draft-task.es6 index 6b5aff8a8..edeaf1c54 100644 --- a/packages/client-app/src/flux/tasks/send-draft-task.es6 +++ b/packages/client-app/src/flux/tasks/send-draft-task.es6 @@ -3,6 +3,8 @@ import AccountStore from '../stores/account-store'; import Task from './task'; import Actions from '../actions'; import SoundRegistry from '../../registries/sound-registry'; +import Attributes from '../attributes'; +import Message from '../models/message'; const OPEN_TRACKING_ID = NylasEnv.packages.pluginIdFor('open-tracking') const LINK_TRACKING_ID = NylasEnv.packages.pluginIdFor('link-tracking') @@ -10,12 +12,32 @@ const LINK_TRACKING_ID = NylasEnv.packages.pluginIdFor('link-tracking') export default class SendDraftTask extends Task { - constructor(draft, {playSound = true, emitError = true, allowMultiSend = true} = {}) { - super(); - this.draft = draft; + static attributes = Object.assign({}, Task.attributes, { + draft: Attributes.Object({ + modelKey: 'draft', + itemClass: Message, + }), + headerMessageId: Attributes.String({ + modelKey: 'headerMessageId', + }), + emitError: Attributes.Boolean({ + modelKey: 'emitError', + }), + playSound: Attributes.Boolean({ + modelKey: 'playSound', + }), + allowMultiSend: Attributes.Boolean({ + modelKey: 'allowMultiSend', + }), + perRecipientBodies: Attributes.Collection({ + modelKey: 'perRecipientBodies', + }), + }); + + constructor({draft, playSound = true, emitError = true, allowMultiSend = true, ...rest} = {}) { + super(rest); this.accountId = (draft || {}).accountId; this.headerMessageId = (draft || {}).headerMessageId; - this.emitError = emitError this.playSound = playSound this.allowMultiSend = allowMultiSend diff --git a/packages/client-app/src/flux/tasks/syncback-category-task.es6 b/packages/client-app/src/flux/tasks/syncback-category-task.es6 index 235c4a31d..90f1c1188 100644 --- a/packages/client-app/src/flux/tasks/syncback-category-task.es6 +++ b/packages/client-app/src/flux/tasks/syncback-category-task.es6 @@ -1,13 +1,25 @@ import Task from './task'; +import Attributes from '../attributes'; export default class SyncbackCategoryTask extends Task { - constructor({existingPath, path, accountId} = {}) { - super() + static attributes = Object.assign({}, Task.attributes, { + path: Attributes.String({ + modelKey: 'path', + }), + existingPath: Attributes.String({ + modelKey: 'existingPath', + }), + created: Attributes.Object({ + modelKey: 'created', + }), + }); + + constructor({existingPath, path, accountId, ...rest} = {}) { + super(rest); this.existingPath = existingPath; this.path = path; this.accountId = accountId; - this.created = null; } label() { diff --git a/packages/client-app/src/flux/tasks/syncback-draft-task.es6 b/packages/client-app/src/flux/tasks/syncback-draft-task.es6 index 8ef2b460a..747e4f7fb 100644 --- a/packages/client-app/src/flux/tasks/syncback-draft-task.es6 +++ b/packages/client-app/src/flux/tasks/syncback-draft-task.es6 @@ -1,8 +1,21 @@ import Task from './task'; +import Attributes from '../attributes'; +import Message from '../models/message'; export default class SyncbackDraftTask extends Task { - constructor(draft) { - super(); + + static attributes = Object.assign({}, Task.attributes, { + headerMessageId: Attributes.String({ + modelKey: 'headerMessageId', + }), + draft: Attributes.Object({ + modelKey: 'draft', + itemClass: Message, + }), + }); + + constructor({draft, ...rest} = {}) { + super(rest); this.draft = draft; this.accountId = (draft || {}).accountId; this.headerMessageId = (draft || {}).headerMessageId; diff --git a/packages/client-app/src/flux/tasks/syncback-metadata-task.es6 b/packages/client-app/src/flux/tasks/syncback-metadata-task.es6 index 01c6c871c..8e62b04e0 100644 --- a/packages/client-app/src/flux/tasks/syncback-metadata-task.es6 +++ b/packages/client-app/src/flux/tasks/syncback-metadata-task.es6 @@ -15,14 +15,6 @@ export default class SyncbackMetadataTask extends SyncbackModelTask { return DatabaseObjectRegistry.get(this.modelClassName); } - isDependentOnTask(otherTask) { - return ( - otherTask instanceof SyncbackMetadataTask && - otherTask.pluginId === this.pluginId && - otherTask.sequentialId < this.sequentialId - ) - } - makeRequest = async (model) => { if (!model.serverId) { throw new Error(`Can't syncback metadata for a ${this.modelClassName} instance that doesn't have a serverId`) diff --git a/packages/client-app/src/flux/tasks/task.es6 b/packages/client-app/src/flux/tasks/task.es6 index f334c8cd1..647db478a 100644 --- a/packages/client-app/src/flux/tasks/task.es6 +++ b/packages/client-app/src/flux/tasks/task.es6 @@ -27,6 +27,9 @@ export default class Task extends Model { queryable: true, modelKey: 'status', }), + source: Attributes.String({ + modelKey: 'source', + }), error: Attributes.Object({ modelKey: 'error', }), @@ -39,13 +42,10 @@ export default class Task extends Model { // `super`. // // On construction, all Tasks instances are given a unique `id`. - constructor() { - super(); - this.version = 1; - this._rememberedToCallSuper = true; - this.id = generateTempId(); + constructor(data) { + super(data); + this.id = this.id || generateTempId(); this.accountId = null; - this.sequentialId = null; // set when queued } // Public: Override to raise exceptions if your task is missing required