diff --git a/spec-nylas/stores/draft-store-spec.coffee b/spec-nylas/stores/draft-store-spec.coffee index 014d94304..13d5a0295 100644 --- a/spec-nylas/stores/draft-store-spec.coffee +++ b/spec-nylas/stores/draft-store-spec.coffee @@ -529,12 +529,12 @@ describe "DraftStore", -> }} it "should return false and call window.close itself", -> - spyOn(DraftStore, '_onBeforeUnloadComplete') + spyOn(atom, 'finishUnload') expect(DraftStore._onBeforeUnload()).toBe(false) - expect(DraftStore._onBeforeUnloadComplete).not.toHaveBeenCalled() + expect(atom.finishUnload).not.toHaveBeenCalled() @resolve() advanceClock(1000) - expect(DraftStore._onBeforeUnloadComplete).toHaveBeenCalled() + expect(atom.finishUnload).toHaveBeenCalled() describe "when drafts return immediately fulfilled commit promises", -> beforeEach -> @@ -546,11 +546,11 @@ describe "DraftStore", -> }} it "should still wait one tick before firing atom.close again", -> - spyOn(DraftStore, '_onBeforeUnloadComplete') + spyOn(atom, 'finishUnload') expect(DraftStore._onBeforeUnload()).toBe(false) - expect(DraftStore._onBeforeUnloadComplete).not.toHaveBeenCalled() + expect(atom.finishUnload).not.toHaveBeenCalled() advanceClock() - expect(DraftStore._onBeforeUnloadComplete).toHaveBeenCalled() + expect(atom.finishUnload).toHaveBeenCalled() describe "when there are no drafts", -> beforeEach -> diff --git a/src/atom.coffee b/src/atom.coffee index 57a4aaa70..219b00bc4 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -341,7 +341,7 @@ class Atom extends Model # content trace visualizer (chrome://tracing). It's like Chromium Developer # Tools Profiler, but for all processes and threads. trace: -> - tracing = require('remote').require('content-tracing') + tracing = remote.require('content-tracing') tracing.startRecording '*', 'record-until-full,enable-sampling,enable-systrace', -> console.log('Tracing started') setTimeout -> @@ -588,7 +588,7 @@ class Atom extends Model if @isValidDimensions(dimensions) dimensions else - screen = remote.require 'screen' + screen = remote.require('screen') {width, height} = screen.getPrimaryDisplay().workAreaSize {x: 0, y: 0, width, height} @@ -881,8 +881,13 @@ class Atom extends Model ipc.send('call-window-method', 'setAutoHideMenuBar', autoHide) ipc.send('call-window-method', 'setMenuBarVisibility', !autoHide) - # Lets multiple components register callbacks. - # The callbacks are expected to return either true or false + # Lets multiple components register beforeUnload callbacks. + # The callbacks are expected to return either true or false. + # + # Note: If you return false to cancel the window close, you /must/ perform + # work and then call finishUnload. We do not support cancelling quit! + # https://phab.nylas.com/D1932#inline-11722 + # onBeforeUnload: (callback) -> @_unloadCallbacks.push(callback) _unloading: -> @@ -896,3 +901,13 @@ class Atom extends Model else console.warn "You registered an `onBeforeUnload` callback that does not return either exactly `true` or `false`. It returned #{returnValue}", callback return continueUnload + + # Call this method to resume the close / quit process if you returned + # false from a onBeforeUnload handler. + # + finishUnload: -> + _.defer => + if remote.getGlobal('application').quitting + remote.quit() + else + @close() diff --git a/src/browser/application.coffee b/src/browser/application.coffee index e9846e383..e8d5350a9 100644 --- a/src/browser/application.coffee +++ b/src/browser/application.coffee @@ -268,11 +268,11 @@ class Application # Called before the app tries to close any windows. app.on 'before-quit', => + # Allow the main window to be closed. + @quitting = true # Destroy hot windows so that they can't block the app from quitting. # (Electron will wait for them to finish loading before quitting.) @windowManager.unregisterAllHotWindows() - # Allow the main window to be closed. - @quitting = true # Called after the app has closed all windows. app.on 'will-quit', => @@ -330,7 +330,7 @@ class Application ipc.on 'action-bridge-rebroadcast-to-main', (event, args...) => mainWindow = @windowManager.mainWindow() - return if not mainWindow + return if not mainWindow or not mainWindow.browserWindow.webContents return if BrowserWindow.fromWebContents(event.sender) is mainWindow mainWindow.browserWindow.webContents.send('action-bridge-message', args...) diff --git a/src/flux/stores/draft-store.coffee b/src/flux/stores/draft-store.coffee index 953e76a59..932ed49de 100644 --- a/src/flux/stores/draft-store.coffee +++ b/src/flux/stores/draft-store.coffee @@ -166,7 +166,7 @@ class DraftStore # handler, so we need to always defer by one tick before re-firing close. Promise.settle(promises).then => @_draftSessions = {} - @_onBeforeUnloadComplete() + atom.finishUnload() # Stop and wait before closing return false @@ -174,10 +174,6 @@ class DraftStore # Continue closing return true - # For better specs - _onBeforeUnloadComplete: => - _.defer -> atom.close() - _onDataChanged: (change) => return unless change.objectClass is Message.name containsDraft = _.some(change.objects, (msg) -> msg.draft)