diff --git a/packages/client-app/internal_packages/composer/lib/action-bar-plugins.jsx b/packages/client-app/internal_packages/composer/lib/action-bar-plugins.jsx index aa68f587c..f4675d19f 100644 --- a/packages/client-app/internal_packages/composer/lib/action-bar-plugins.jsx +++ b/packages/client-app/internal_packages/composer/lib/action-bar-plugins.jsx @@ -68,7 +68,7 @@ export default class ActionBarPlugins extends React.Component { exposedProps={{ draft: this.props.draft, threadId: this.props.draft.threadId, - draftId: this.props.draft.id, + headerMessageId: this.props.draft.headerMessageId, session: this.props.session, isValidDraft: this.props.isValidDraft, }} diff --git a/packages/client-app/internal_packages/composer/lib/composer-editor.jsx b/packages/client-app/internal_packages/composer/lib/composer-editor.jsx index 59f8e518e..0affaa1f2 100644 --- a/packages/client-app/internal_packages/composer/lib/composer-editor.jsx +++ b/packages/client-app/internal_packages/composer/lib/composer-editor.jsx @@ -14,7 +14,7 @@ import {DropZone, ScrollRegion, Contenteditable} from 'nylas-component-kit'; * @param {object} props - props for ComposerEditor * @param {string} props.body - Html string with the draft content to be * rendered by the editor - * @param {string} props.draftId - Id of the draft being currently edited + * @param {string} props.headerMessageId - Id of the draft being currently edited * @param {object} props.parentActions - Object containg helper actions * associated with the parent container * @param {props.parentActions.getComposerBoundingRect} props.parentActions.getComposerBoundingRect @@ -64,7 +64,7 @@ class ComposerEditor extends Component { */ static propTypes = { body: PropTypes.string.isRequired, - draftId: PropTypes.string, + headerMessageId: PropTypes.string, onFilePaste: PropTypes.func, onBodyChanged: PropTypes.func, parentActions: PropTypes.shape({ @@ -185,7 +185,7 @@ class ComposerEditor extends Component { _scrollToBottom = () => { this.props.parentActions.scrollTo({ - id: this.props.draftId, + headerMessageId: this.props.headerMessageId, position: ScrollRegion.ScrollPosition.Bottom, }); }; diff --git a/packages/client-app/internal_packages/composer/lib/composer-header.jsx b/packages/client-app/internal_packages/composer/lib/composer-header.jsx index 06d40458a..0f303e898 100644 --- a/packages/client-app/internal_packages/composer/lib/composer-header.jsx +++ b/packages/client-app/internal_packages/composer/lib/composer-header.jsx @@ -243,7 +243,7 @@ export default class ComposerHeader extends React.Component { draft, session, value: draft.subject, - draftId: draft.id, + headerMessageId: draft.headerMessageId, onSubjectChange: this._onSubjectChange, }} requiredMethods={['focus']} diff --git a/packages/client-app/internal_packages/message-list/lib/message-list.cjsx b/packages/client-app/internal_packages/message-list/lib/message-list.cjsx index 2a99747a1..ea994fcec 100644 --- a/packages/client-app/internal_packages/message-list/lib/message-list.cjsx +++ b/packages/client-app/internal_packages/message-list/lib/message-list.cjsx @@ -76,9 +76,9 @@ class MessageList extends React.Component componentDidMount: => @_unsubscribers = [] @_unsubscribers.push MessageStore.listen @_onChange - @_unsubscribers.push Actions.focusDraft.listen ({draftId}) => - Utils.waitFor( => @_getMessageContainer(draftId)?).then => - @_focusDraft(@_getMessageContainer(draftId)) + @_unsubscribers.push Actions.focusDraft.listen ({headerMessageId}) => + Utils.waitFor( => @_getMessageContainer(headerMessageId)?).then => + @_focusDraft(@_getMessageContainer(headerMessageId)) .catch => componentWillUnmount: => @@ -129,8 +129,8 @@ class MessageList extends React.Component handlers - _getMessageContainer: (id) => - @refs["message-container-#{id}"] + _getMessageContainer: (headerMessageId) => + @refs["message-container-#{headerMessageId}"] _focusDraft: (draftElement) => # Note: We don't want the contenteditable view competing for scroll offset, @@ -306,7 +306,7 @@ class MessageList extends React.Component elements.push( updated = DatabaseWriter.prototype.persistModel.calls[0].args[0] expect(updated.body).toBe "123" - # Note: Syncback temporarily disabled - # - # it "queues a SyncbackDraftTask", -> - # spyOn(DatabaseStore, "run").andReturn(Promise.resolve(@draft)) - # @session.changes.add({body: "123"}) - # waitsForPromise => - # @session.changes.commit().then => - # expect(Actions.queueTask).toHaveBeenCalled() - # task = Actions.queueTask.calls[0].args[0] - # expect(task.draftId).toBe "client-id" - it "doesn't queues a SyncbackDraftTask if no Syncback is passed", -> spyOn(DatabaseStore, "run").andReturn(Promise.resolve(@draft)) waitsForPromise => diff --git a/packages/client-app/spec/tasks/syncback-draft-task-spec.es6 b/packages/client-app/spec/tasks/syncback-draft-task-spec.es6 deleted file mode 100644 index 98b096067..000000000 --- a/packages/client-app/spec/tasks/syncback-draft-task-spec.es6 +++ /dev/null @@ -1,129 +0,0 @@ -import _ from 'underscore'; -import { - DatabaseWriter, - SyncbackDraftTask, - SyncbackMetadataTask, - DatabaseStore, - AccountStore, - TaskQueue, - Contact, - Message, - Account, - Actions, - Task, - APIError, - NylasAPI, - NylasAPIRequest, -} from 'nylas-exports'; - -const inboxError = { - message: "No draft with public id bvn4aydxuyqlbmzowh4wraysg", - type: "invalid_request_error", -}; - -const testData = { - to: [new Contact({name: "Ben Gotow", email: "benthis.nylas.com"})], - from: [new Contact({name: "Evan Morikawa", email: "evanthis.nylas.com"})], - date: new Date(), - draft: true, - subject: "Test", - accountId: "abc123", - body: '123', -}; - -const localDraft = () => new Message(_.extend({}, testData, { - clientId: "local-id", -})); - -const remoteDraft = () => new Message(_.extend({}, testData, { - clientId: "local-id", - id: "remoteid1234", - threadId: '1234', - version: 2, -})); - -xdescribe('SyncbackDraftTask', function syncbackDraftTask() { - beforeEach(() => { - spyOn(AccountStore, "accountForEmail").andCallFake((email) => - new Account({clientId: 'local-abc123', id: 'abc123', emailAddress: email}) - ); - - spyOn(DatabaseStore, "run").andCallFake((query) => { - const clientId = query.matcherValueForModelKey('clientId') - if (clientId === "localDraftId") { - return Promise.resolve(localDraft()); - } - if (clientId === "remoteDraftId") { - return Promise.resolve(remoteDraft()); - } - if (clientId === "missingDraftId") { - return Promise.resolve(); - } - return Promise.resolve(); - }); - - spyOn(NylasAPI, 'incrementRemoteChangeLock'); - spyOn(NylasAPI, 'decrementRemoteChangeLock'); - spyOn(DatabaseWriter.prototype, "persistModel").andReturn(Promise.resolve()); - }); - - describe("queueing multiple tasks", () => { - beforeEach(() => { - this.taskA = new SyncbackDraftTask("draft-123"); - this.taskB = new SyncbackDraftTask("draft-123"); - this.taskC = new SyncbackDraftTask("draft-123"); - this.taskOther = new SyncbackDraftTask("draft-456"); - - this.taskA.sequentialId = 0; - this.taskB.sequentialId = 1; - this.taskC.sequentialId = 2; - TaskQueue._queue = []; - }); - - it("dequeues other SyncbackDraftTasks that haven't started yet", () => { - // Task A is taking forever, B is waiting on it, and C gets queued. - for (const t of [this.taskA, this.taskB, this.taskOther]) { - t.queueState.localComplete = true; - } - - // taskA has already started This should NOT get dequeued - this.taskA.queueState.isProcessing = true; - - // taskB hasn't started yet! This should get dequeued - this.taskB.queueState.isProcessing = false; - - // taskOther, while unstarted, doesn't match the draftId and should - // not get dequeued - this.taskOther.queueState.isProcessing = false; - - TaskQueue._queue = [this.taskA, this.taskB, this.taskOther]; - spyOn(this.taskC, "runLocal").andReturn(Promise.resolve()); - - TaskQueue.enqueue(this.taskC); - - // Note that taskB is gone, taskOther was untouched, and taskC was - // added. - expect(TaskQueue._queue).toEqual = [this.taskA, this.taskOther, this.taskC]; - - expect(this.taskC.runLocal).toHaveBeenCalled(); - }); - - it("waits for any other inflight tasks to finish or error", () => { - this.taskA.queueState.localComplete = true; - this.taskA.queueState.isProcessing = true; - this.taskB.queueState.localComplete = true; - spyOn(this.taskB, "runRemote").andReturn(Promise.resolve()); - - TaskQueue._queue = [this.taskA, this.taskB]; - - // Since taskA has isProcessing set to true, it will just be passed - // over. We expect taskB to fail the `_taskIsBlocked` test - TaskQueue._processQueue(); - advanceClock(100); - expect(TaskQueue._queue).toEqual([this.taskA, this.taskB]); - expect(this.taskA.queueState.isProcessing).toBe(true); - expect(this.taskB.queueState.isProcessing).toBe(false); - expect(this.taskB.runRemote).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/packages/client-app/src/components/participants-text-field.jsx b/packages/client-app/src/components/participants-text-field.jsx index 927c21d86..951261c65 100644 --- a/packages/client-app/src/components/participants-text-field.jsx +++ b/packages/client-app/src/components/participants-text-field.jsx @@ -203,7 +203,7 @@ export default class ParticipantsTextField extends React.Component { const classSet = { [this.props.field]: true, }; - const draftId = this.props.draft ? this.props.draft.id : null + const headerMessageId = this.props.draft ? this.props.draft.headerMessageId : null // TODO Ahh now that this component is part of the component kit this // injected region feels out of place return ( @@ -232,7 +232,7 @@ export default class ParticipantsTextField extends React.Component { menuPrompt: this.props.field, field: this.props.field, draft: this.props.draft, - draftId: draftId, + headerMessageId: headerMessageId, session: this.props.session, }} /> 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 26b74302f..8c7bb3a30 100644 --- a/packages/client-app/src/flux/tasks/send-draft-task.es6 +++ b/packages/client-app/src/flux/tasks/send-draft-task.es6 @@ -18,8 +18,8 @@ const LINK_TRACKING_ID = NylasEnv.packages.pluginIdFor('link-tracking') export default class SendDraftTask extends BaseDraftTask { - constructor(draftId, {playSound = true, emitError = true, allowMultiSend = true} = {}) { - super(draftId); + constructor(headerMessageId, {playSound = true, emitError = true, allowMultiSend = true} = {}) { + super(headerMessageId); this.draft = null; this.message = null; this.emitError = emitError @@ -191,7 +191,7 @@ export default class SendDraftTask extends BaseDraftTask { onSuccess = () => { Actions.recordUserEvent("Draft Sent") - Actions.draftDeliverySucceeded({message: this.message, messageId: this.message.id, draftId: this.draft.id}); + Actions.draftDeliverySucceeded({message: this.message, messageId: this.message.id, headerMessageId: this.draft.headerMessageId}); // TODO we shouldn't need to do this anymore NylasAPIHelpers.makeDraftDeletionRequest(this.draft); @@ -239,14 +239,14 @@ export default class SendDraftTask extends BaseDraftTask { if (err instanceof RequestEnsureOnceError) { Actions.draftDeliveryFailed({ threadId: this.draft.threadId, - draftId: this.draft.id, + headerMessageId: this.draft.headerMessageId, errorMessage: `WARNING: Your message MIGHT have sent. We encountered a network problem while the send was in progress. Please wait a few minutes then check your sent folder and try again if necessary.`, errorDetail: `Please email support@nylas.com if you see this error message.`, }); } else { Actions.draftDeliveryFailed({ threadId: this.draft.threadId, - draftId: this.draft.id, + headerMessageId: this.draft.headerMessageId, errorMessage: message, errorDetail: err.message + (err.error ? err.error.stack : '') + err.stack, });