feat(extension): async extensions

Summary:
WIP:

This is a quick patch for Drew to make extensions async

We'll need to think through the upgrade/deprecation plan to roll out async
extensions across all of our APIs.

Test Plan: TODO

Reviewers: drew, evan, bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D2392
This commit is contained in:
Evan Morikawa 2015-12-23 17:25:30 -08:00 committed by Juan Tejada
parent 2800cc0dc8
commit 11b731891f
7 changed files with 36 additions and 22 deletions

View file

@ -41,5 +41,6 @@ class ProductsExtension extends ComposerExtension
bodyWithWarning = draft.body += "<br>This email \
contains competitor's product names \
or trademarks used in context."
session.changes.add(body: bodyWithWarning)
return session.changes.add(body: bodyWithWarning)
else return Promise.resolve()
```

View file

@ -1,12 +1,13 @@
{ComposerExtension} = require 'nylas-exports'
request = require 'request'
post = Promise.promisify(request.post, multiArgs: true)
class AvailabilityComposerExtension extends ComposerExtension
# When subclassing the ComposerExtension, you can add your own custom logic
# to execute before a draft is sent in the @finalizeSessionBeforeSending
# method. Here, we're registering the events before we send the draft.
@finalizeSessionBeforeSending: ({session}) ->
@finalizeSessionBeforeSending: (session) ->
body = session.draft().body
participants = session.draft().participants()
sender = session.draft().from
@ -18,8 +19,19 @@ class AvailabilityComposerExtension extends ComposerExtension
data.attendees = participants.map (p) ->
name: p.name, email: p.email, isSender: p.isMe()
serverUrl = "https://quickschedule.herokuapp.com/register-events"
request.post {url: serverUrl, body: JSON.stringify(data)}, (error, resp, data) =>
console.log(error,resp,data)
post({url: serverUrl, body: JSON.stringify(data)})
.then (args) =>
data = args[1]
return data
.catch (error) ->
dialog = require('remote').require('dialog')
dialog.showErrorBox('Error creating QuickSchedule event',
"There was a problem connecting to the QuickSchedule server. Make sure you're connected to the internet and "+
"try sending again. If problems persist, contact the N1 team (using the blue question icon at the bottom right "+
"of your inbox) and we'll get right on it!")
Promise.reject(error)
else
Promise.resolve()
module.exports = AvailabilityComposerExtension

View file

@ -123,7 +123,7 @@ class SpellcheckComposerExtension extends ComposerExtension
body = session.draft().body
clean = body.replace(/<\/?spelling[^>]*>/g, '')
if body != clean
session.changes.add(body: clean)
return session.changes.add(body: clean)
SpellcheckComposerExtension.SpellcheckCache = SpellcheckCache

View file

@ -25,9 +25,10 @@ describe "SpellcheckComposerExtension", ->
draft: ->
body: expectedHTML
changes:
add: jasmine.createSpy('add')
add: jasmine.createSpy('add').andReturn Promise.resolve()
SpellcheckComposerExtension.finalizeSessionBeforeSending({session})
expect(session.changes.add).toHaveBeenCalledWith(body: initialHTML)
waitsForPromise ->
SpellcheckComposerExtension.finalizeSessionBeforeSending(session).then ->
expect(session.changes.add).toHaveBeenCalledWith(body: initialHTML)
module.exports = SpellcheckComposerExtension

View file

@ -124,7 +124,7 @@ describe "ComposerView", ->
# `componentWillMount`, we manually call sessionForClientId to make this
# part of the test synchronous. We need to make the `then` block of the
# sessionForClientId do nothing so `_setupSession` is not called twice!
spyOn(DraftStore, "sessionForClientId").andCallFake -> then: ->
spyOn(DraftStore, "sessionForClientId").andReturn then: -> then: ->
useFullDraft = ->
useDraft.call @,

View file

@ -28,8 +28,8 @@ session you receive in {::finalizeSessionBeforeSending} is for the same
draft you previously received in {::warningsForSending}, etc.
The ComposerExtension API does not currently expose any asynchronous or
{Promise}-based APIs. This will likely change in the future. If you have
a use-case for a ComposerExtension that is not possible with the current
{Promise}-based APIs, except for finalizeSessionBeforeSending. This will likely
change in the future. If you havea use-case for a ComposerExtension that is not possible with the current
API, please let us know.
Section: Extensions
@ -96,6 +96,8 @@ class ComposerExtension extends ContenteditableExtension
the draft is sent. This method gives you an opportunity to make any
final substitutions or changes after any {::warningsForSending} have
been displayed.
If you want to perform asynchronous work, you this method can return a promise,
however, returning a Promise is not required.
- `session`: A {DraftStoreProxy} for the draft.
@ -110,7 +112,7 @@ class ComposerExtension extends ContenteditableExtension
session.changes.add(body: clean)
```
###
@finalizeSessionBeforeSending: ({session}) ->
return
@finalizeSessionBeforeSending: (session) ->
return Promise.resolve(session)
module.exports = ComposerExtension

View file

@ -496,10 +496,9 @@ class DraftStore
# point there are still unpersisted changes in the DraftStoreProxy. If
# we `trigger`, we'll briefly display the wrong version of the draft
# as if it was sending.
@sessionForClientId(draftClientId).then (session) =>
@_runExtensionsBeforeSend(session)
@sessionForClientId(draftClientId)
.then(@_runExtensionsBeforeSend)
.then (session) =>
# Immediately save any pending changes so we don't save after
# sending
#
@ -511,7 +510,6 @@ class DraftStore
# committed to the Database since we'll look them up again just
# before send.
session.changes.commit(force: true, noSyncback: true).then =>
# We unfortunately can't give the SendDraftTask the raw draft JSON
# data because there may still be pending tasks (like a
# {FileUploadTask}) that will continue to update the draft data.
@ -532,10 +530,10 @@ class DraftStore
NylasEnv.getWindowType() is "composer"
# Give third-party plugins an opportunity to sanitize draft data
_runExtensionsBeforeSend: (session) ->
for extension in @extensions()
continue unless extension.finalizeSessionBeforeSending
extension.finalizeSessionBeforeSending({session})
_runExtensionsBeforeSend: (session) =>
Promise.each @extensions(), (ext) ->
ext.finalizeSessionBeforeSending(session)
.return(session)
_onRemoveFile: ({file, messageClientId}) =>
@sessionForClientId(messageClientId).then (session) ->