From b515a5e0e26f96e42bc3efd6ebd948f029f3f5de Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Thu, 17 Mar 2016 13:14:55 -0700 Subject: [PATCH] feat(dock-icon): Drop files to attach them to new msg Summary: Fix specs Fix responding to mailto, files at launch It's super important that `window:loaded` is /not/ sent from index.js because `loadSettings.bootstrapScript` is async and nothing is actually loaded yet. This was causing the app to dispatch the mailto:// links into the main window before a DraftStore existed. I think this was necessary at one point because we had NylasWindows not using a bootstrapScript? Should not be here anymore... Test Plan: Run a few new tests Reviewers: juan, evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D2737 --- build/resources/mac/nylas-Info.plist | 12 +++++++++++ spec/stores/draft-store-spec.coffee | 25 +++++++++++++++++++++++ spec/stores/file-upload-store-spec.coffee | 1 + src/browser/application.coffee | 14 +++++++++---- src/browser/main.coffee | 10 +++++++-- src/browser/main.js | 14 ++++++++++--- src/browser/nylas-window.coffee | 2 +- src/flux/stores/draft-store.coffee | 16 +++++++++++++++ src/flux/stores/file-upload-store.coffee | 6 ++++++ static/index.js | 1 - 10 files changed, 90 insertions(+), 11 deletions(-) diff --git a/build/resources/mac/nylas-Info.plist b/build/resources/mac/nylas-Info.plist index dcaa24f30..983fe4c7c 100644 --- a/build/resources/mac/nylas-Info.plist +++ b/build/resources/mac/nylas-Info.plist @@ -61,6 +61,18 @@ CFBundleDocumentTypes + + LSHandlerRank + Alternate + CFBundleTypeRole + Viewer + CFBundleTypeName + File + CFBundleTypeExtensions + + * + + diff --git a/spec/stores/draft-store-spec.coffee b/spec/stores/draft-store-spec.coffee index a0e67ab2e..488cf7761 100644 --- a/spec/stores/draft-store-spec.coffee +++ b/spec/stores/draft-store-spec.coffee @@ -4,6 +4,7 @@ Actions, Contact, Message, + Account, DraftStore, AccountStore, DatabaseStore, @@ -993,3 +994,27 @@ describe "DraftStore", -> expect(actual instanceof Contact).toBe(true) expect(actual.email).toEqual(expected.email) expect(actual.name).toEqual(expected.name) + + describe "mailfiles handling", -> + it "should popout a new draft", -> + defaultMe = new Contact() + spyOn(DraftStore, '_onPopoutDraftClientId') + spyOn(DatabaseTransaction.prototype, 'persistModel') + spyOn(Account.prototype, 'defaultMe').andReturn(defaultMe) + spyOn(Actions, 'addAttachment') + DraftStore._onHandleMailFiles({}, ['/Users/ben/file1.png', '/Users/ben/file2.png']) + waitsFor -> + DatabaseTransaction.prototype.persistModel.callCount > 0 + runs -> + {body, subject, from} = DatabaseTransaction.prototype.persistModel.calls[0].args[0] + expect({body, subject, from}).toEqual({body:'', subject:'', from: [defaultMe]}) + expect(DraftStore._onPopoutDraftClientId).toHaveBeenCalled() + + it "should call addAttachment for each provided file path", -> + spyOn(Actions, 'addAttachment') + DraftStore._onHandleMailFiles({}, ['/Users/ben/file1.png', '/Users/ben/file2.png']) + waitsFor -> + Actions.addAttachment.callCount is 2 + runs -> + expect(Actions.addAttachment.calls[0].args[0].filePath).toEqual('/Users/ben/file1.png') + expect(Actions.addAttachment.calls[1].args[0].filePath).toEqual('/Users/ben/file2.png') diff --git a/spec/stores/file-upload-store-spec.coffee b/spec/stores/file-upload-store-spec.coffee index 681036ca3..98b8b1b66 100644 --- a/spec/stores/file-upload-store-spec.coffee +++ b/spec/stores/file-upload-store-spec.coffee @@ -19,6 +19,7 @@ describe 'FileUploadStore', -> @session = changes: add: jasmine.createSpy('session.changes.add') + commit: -> draft: => @draft spyOn(NylasEnv, "isMainWindow").andReturn true spyOn(FileUploadStore, "_onAttachFileError").andCallFake (msg) -> diff --git a/src/browser/application.coffee b/src/browser/application.coffee index e8b999d25..0213ed2f6 100644 --- a/src/browser/application.coffee +++ b/src/browser/application.coffee @@ -130,14 +130,16 @@ class Application @launchWithOptions(options) # Opens a new window based on the options provided. - launchWithOptions: ({urlsToOpen, specMode, devMode, safeMode, specDirectory, specFilePattern, logFile, showSpecsInWindow}) -> + launchWithOptions: ({urlsToOpen, pathsToOpen, specMode, safeMode, specDirectory, specFilePattern, logFile, showSpecsInWindow}) -> if specMode exitWhenDone = true @runSpecs({exitWhenDone, showSpecsInWindow, @resourcePath, specDirectory, specFilePattern, logFile}) else @openWindowsForTokenState() - for urlToOpen in (urlsToOpen || []) - @openUrl(urlToOpen) + if pathsToOpen instanceof Array + @openComposerWithFiles(pathsToOpen) + if urlsToOpen instanceof Array + @openUrl(urlToOpen) for urlToOpen in urlsToOpen # Creates server to listen for additional N1 application launches. # @@ -339,7 +341,8 @@ class Application @setDatabasePhase('close') @deleteSocketFile() - app.on 'open-file', (event, pathToOpen) -> + app.on 'open-file', (event, pathToOpen) => + @openComposerWithFiles([pathToOpen]) event.preventDefault() app.on 'open-url', (event, urlToOpen) => @@ -515,6 +518,9 @@ class Application else console.log "Ignoring unknown URL type: #{urlToOpen}" + openComposerWithFiles: (pathsToOpen) -> + @windowManager.sendToMainWindow('mailfiles', pathsToOpen) + # Opens up a new {NylasWindow} to run specs within. # # options - diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 9d6341200..74e23939f 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -19,11 +19,16 @@ start = -> addUrlToOpen = (event, urlToOpen) -> event.preventDefault() args.urlsToOpen.push(urlToOpen) - app.on 'open-url', addUrlToOpen + addFileToOpen = (event, fileToOpen) -> + event.preventDefault() + args.filesToOpen.push(fileToOpen) + app.on 'open-file', addFileToOpen + app.on 'ready', -> app.removeListener 'open-url', addUrlToOpen + app.removeListener 'open-file', addFileToOpen Application = require path.join(args.resourcePath, 'src', 'browser', 'application') Application.open(args) @@ -141,11 +146,12 @@ parseCommandLine = -> path.dirname(path.dirname(__dirname))) urlsToOpen = [] + filesToOpen = [] # On Yosemite the $PATH is not inherited by the "open" command, so we # have to explicitly pass it by command line, see http://git.io/YC8_Ew. process.env.PATH = args['path-environment'] if args['path-environment'] - return {version, devMode, background, logFile, specMode, safeMode, configDirPath, specDirectory, specFilePattern, showSpecsInWindow, resourcePath, urlsToOpen} + return {version, devMode, background, logFile, specMode, safeMode, configDirPath, specDirectory, specFilePattern, showSpecsInWindow, resourcePath, urlsToOpen, filesToOpen} start() diff --git a/src/browser/main.js b/src/browser/main.js index d7ceaa85b..9f8bc9dbe 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -13,7 +13,7 @@ mkdirp = require('mkdirp'); start = function() { - var addUrlToOpen, args, configDirPath; + var addFileToOpen, addUrlToOpen, args, configDirPath; args = parseCommandLine(); global.errorLogger = setupErrorLogger(args); configDirPath = setupConfigDir(args); @@ -28,9 +28,15 @@ return args.urlsToOpen.push(urlToOpen); }; app.on('open-url', addUrlToOpen); + addFileToOpen = function(event, fileToOpen) { + event.preventDefault(); + return args.filesToOpen.push(fileToOpen); + }; + app.on('open-file', addFileToOpen); return app.on('ready', function() { var Application; app.removeListener('open-url', addUrlToOpen); + app.removeListener('open-file', addFileToOpen); Application = require(path.join(args.resourcePath, 'src', 'browser', 'application')); Application.open(args); if (!args.specMode) { @@ -103,7 +109,7 @@ }; parseCommandLine = function() { - var args, background, configDirPath, devMode, logFile, options, ref, resourcePath, safeMode, showSpecsInWindow, specDirectory, specFilePattern, specMode, urlsToOpen, version; + var args, background, configDirPath, devMode, filesToOpen, logFile, options, ref, resourcePath, safeMode, showSpecsInWindow, specDirectory, specFilePattern, specMode, urlsToOpen, version; version = app.getVersion(); options = declareOptions(process.argv.slice(1)); args = options.argv; @@ -126,6 +132,7 @@ showSpecsInWindow = specMode === "window"; resourcePath = path.resolve((ref = args['resource-path']) != null ? ref : path.dirname(path.dirname(__dirname))); urlsToOpen = []; + filesToOpen = []; if (args['path-environment']) { process.env.PATH = args['path-environment']; } @@ -141,7 +148,8 @@ specFilePattern: specFilePattern, showSpecsInWindow: showSpecsInWindow, resourcePath: resourcePath, - urlsToOpen: urlsToOpen + urlsToOpen: urlsToOpen, + filesToOpen: filesToOpen }; }; diff --git a/src/browser/nylas-window.coffee b/src/browser/nylas-window.coffee index 21e0085da..adfeea269 100644 --- a/src/browser/nylas-window.coffee +++ b/src/browser/nylas-window.coffee @@ -107,12 +107,12 @@ class NylasWindow @setLoadSettings(loadSettings) @browserWindow.once 'window:loaded', => - @emit 'window:loaded' @loaded = true if @mainWindow @browserWindow.setResizable(true) if @browserWindow.loadSettingsChangedSinceGetURL @browserWindow.webContents.send('load-settings-changed', @browserWindow.loadSettings) + @emit 'window:loaded' @browserWindow.loadURL(@getURL(loadSettings)) @browserWindow.focusOnWebView() if @isSpec diff --git a/src/flux/stores/draft-store.coffee b/src/flux/stores/draft-store.coffee index e3856c461..3ff728614 100644 --- a/src/flux/stores/draft-store.coffee +++ b/src/flux/stores/draft-store.coffee @@ -102,6 +102,7 @@ class DraftStore @_draftsSending = {} ipcRenderer.on 'mailto', @_onHandleMailtoLink + ipcRenderer.on 'mailfiles', @_onHandleMailFiles ######### PUBLIC ####################################################### @@ -492,6 +493,21 @@ class DraftStore @_finalizeAndPersistNewMessage(draft).then ({draftClientId}) => @_onPopoutDraftClientId(draftClientId) + _onHandleMailFiles: (event, paths) => + account = @_getAccountForNewMessage() + draft = new Message + body: '' + subject: '' + from: [account.defaultMe()] + date: (new Date) + draft: true + pristine: true + accountId: account.id + @_finalizeAndPersistNewMessage(draft).then ({draftClientId}) => + for path in paths + Actions.addAttachment({filePath: path, messageClientId: draftClientId}) + @_onPopoutDraftClientId(draftClientId) + _onDestroyDraft: (draftClientId) => session = @_draftSessions[draftClientId] diff --git a/src/flux/stores/file-upload-store.coffee b/src/flux/stores/file-upload-store.coffee index d282f8e47..ffd8e0b89 100644 --- a/src/flux/stores/file-upload-store.coffee +++ b/src/flux/stores/file-upload-store.coffee @@ -144,4 +144,10 @@ class FileUploadStore extends NylasStore uploads = changeFunction(session.draft().uploads) session.changes.add({uploads}) + # In some scenarios (like dropping attachments on the dock icon), files + # are added to drafts which may be open in another composer window. + # Committing here ensures the files appear immediately, no matter where the + # user is now viewing the draft. + session.changes.commit() + module.exports = new FileUploadStore() diff --git a/static/index.js b/static/index.js index 711a4a7ef..d9785ca62 100644 --- a/static/index.js +++ b/static/index.js @@ -65,7 +65,6 @@ function setupWindow (loadSettings) { setupCsonCache(CompileCache.getCacheDirectory()) require(loadSettings.bootstrapScript) - require('electron').ipcRenderer.send('window-command', 'window:loaded') } function setupCsonCache (cacheDir) {