feat(composer): popout only closes when message sending succeeds

Summary:
This is a WIP to fix the blatant case of messages dissapearing when
there's an error. It's pending a better through through notification UI

Test Plan: edgehill --test

Reviewers: bengotow

Reviewed By: bengotow

Differential Revision: https://review.inboxapp.com/D1270
This commit is contained in:
Evan Morikawa 2015-03-10 09:52:47 -07:00
parent 2189f4f2b7
commit 6ec84561c4
6 changed files with 41 additions and 11 deletions

View file

@ -47,6 +47,8 @@ NotificationStore = Reflux.createStore
@_removeNotification(notification)() @_removeNotification(notification)()
@listenTo Actions.postNotification, (data) => @listenTo Actions.postNotification, (data) =>
@_postNotification(new Notification(data)) @_postNotification(new Notification(data))
@listenTo Actions.multiWindowNotification, (data={}, context={}) =>
@_postNotification(new Notification(data)) if @_inWindowContext(context)
######### PUBLIC ####################################################### ######### PUBLIC #######################################################
@ -82,3 +84,8 @@ NotificationStore = Reflux.createStore
console.log "Removed #{notification}" if VERBOSE console.log "Removed #{notification}" if VERBOSE
delete @_notifications[notification.id] delete @_notifications[notification.id]
@trigger() @trigger()
# If the window matches the given context then we can show a
# notification.
_inWindowContext: (context={}) ->
return true

View file

@ -16,7 +16,15 @@ globalActions = [
"uploadStateChanged", "uploadStateChanged",
"fileAborted", "fileAborted",
"downloadStateChanged", "downloadStateChanged",
"fileUploaded" "fileUploaded",
"multiWindowNotification",
# Draft actions
"sendDraftSuccess",
"sendDraftError",
"destroyDraftSuccess",
"destroyDraftError"
] ]
# These actions are rebroadcast through the ActionBridge to the # These actions are rebroadcast through the ActionBridge to the

View file

@ -39,6 +39,8 @@ DraftStore = Reflux.createStore
@listenTo Actions.removeFile, @_onRemoveFile @listenTo Actions.removeFile, @_onRemoveFile
@listenTo Actions.attachFileComplete, @_onAttachFileComplete @listenTo Actions.attachFileComplete, @_onAttachFileComplete
@listenTo Actions.sendDraftSuccess, @_closeWindow
@listenTo Actions.destroyDraftSuccess, @_closeWindow
@_drafts = [] @_drafts = []
@_draftSessions = {} @_draftSessions = {}
@ -154,6 +156,12 @@ DraftStore = Reflux.createStore
DatabaseStore.persistModel(draft) DatabaseStore.persistModel(draft)
# We only want to close the popout window if we're sure various draft
# actions succeeded.
_closeWindow: (draftLocalId) ->
if atom.state.mode is "composer" and @_draftSessions[draftLocalId]?
atom.close()
# The logic to create a new Draft used to be in the DraftStore (which is # The logic to create a new Draft used to be in the DraftStore (which is
# where it should be). It got moved to composer/lib/main.cjsx becaues # where it should be). It got moved to composer/lib/main.cjsx becaues
# of an obscure atom-shell/Chrome bug whereby database requests firing right # of an obscure atom-shell/Chrome bug whereby database requests firing right
@ -173,7 +181,6 @@ DraftStore = Reflux.createStore
# Queue the task to destroy the draft # Queue the task to destroy the draft
Actions.queueTask(new DestroyDraftTask(draftLocalId)) Actions.queueTask(new DestroyDraftTask(draftLocalId))
atom.close() if atom.state.mode is "composer"
_onSendDraft: (draftLocalId) -> _onSendDraft: (draftLocalId) ->
# Immediately save any pending changes so we don't save after sending # Immediately save any pending changes so we don't save after sending
@ -181,7 +188,6 @@ DraftStore = Reflux.createStore
save.then -> save.then ->
# Queue the task to send the draft # Queue the task to send the draft
Actions.queueTask(new SendDraftTask(draftLocalId)) Actions.queueTask(new SendDraftTask(draftLocalId))
atom.close() if atom.state.mode is "composer"
_onAttachFileComplete: ({file, messageLocalId}) -> _onAttachFileComplete: ({file, messageLocalId}) ->
@sessionForLocalId(messageLocalId).prepare().then (proxy) -> @sessionForLocalId(messageLocalId).prepare().then (proxy) ->

View file

@ -42,7 +42,9 @@ class DestroyDraftTask extends Task
body: body:
version: @draft.version version: @draft.version
returnsModel: false returnsModel: false
success: resolve success: (args...) =>
Actions.destroyDraftSuccess(@draftLocalId)
resolve(args...)
error: reject error: reject
onAPIError: (apiError) -> onAPIError: (apiError) ->
@ -55,6 +57,7 @@ class DestroyDraftTask extends Task
# do but finish # do but finish
return true return true
else else
Actions.destroyDraftError(@draftLocalId)
@_rollbackLocal() @_rollbackLocal()
onOtherError: -> Promise.resolve() onOtherError: -> Promise.resolve()

View file

@ -23,13 +23,14 @@ class SendDraftTask extends Task
# already sent when they haven't! # already sent when they haven't!
return Promise.reject("Attempt to call SendDraftTask.performLocal without @draftLocalId") unless @draftLocalId return Promise.reject("Attempt to call SendDraftTask.performLocal without @draftLocalId") unless @draftLocalId
Actions.postNotification({message: "Sending message…", type: 'info'}) Actions.postNotification({message: "Sending message…", type: 'info'})
Promise.resolve() Promise.resolve()
performRemote: -> performRemote: ->
new Promise (resolve, reject) => new Promise (resolve, reject) =>
# Fetch the latest draft data to make sure we make the request with the most # Fetch the latest draft data to make sure we make the request with the most
# recent draft version # recent draft version
DatabaseStore.findByLocalId(Message, @draftLocalId).then (draft) -> DatabaseStore.findByLocalId(Message, @draftLocalId).then (draft) =>
# The draft may have been deleted by another task. Nothing we can do. # The draft may have been deleted by another task. Nothing we can do.
return reject(new Error("We couldn't find the saved draft.")) unless draft return reject(new Error("We couldn't find the saved draft.")) unless draft
@ -45,25 +46,30 @@ class SendDraftTask extends Task
method: 'POST' method: 'POST'
body: body body: body
returnsModel: true returnsModel: true
success: -> success: =>
atom.playSound('mail_sent.ogg') atom.playSound('mail_sent.ogg')
Actions.postNotification({message: "Sent!", type: 'success'}) Actions.postNotification({message: "Sent!", type: 'success'})
Actions.sendDraftSuccess(@draftLocalId)
DatabaseStore.unpersistModel(draft).then(resolve) DatabaseStore.unpersistModel(draft).then(resolve)
error: reject error: reject
.catch(reject) .catch(reject)
onAPIError: -> onAPIError: (apiError) ->
msg = "Our server is having problems. Your message has not been sent." msg = apiError.message ? "Our server is having problems. Your message has not been sent."
Actions.sendDraftError(@draftLocalId, msg)
@notifyErrorMessage(msg) @notifyErrorMessage(msg)
onOtherError: -> onOtherError: ->
msg = "We had a serious issue while sending. Your message has not been sent." msg = "We had a serious issue while sending. Your message has not been sent."
Actions.sendDraftError(@draftLocalId, msg)
@notifyErrorMessage(msg) @notifyErrorMessage(msg)
onTimeoutError: -> onTimeoutError: ->
msg = "The server is taking an abnormally long time to respond. Your message has not been sent." msg = "The server is taking an abnormally long time to respond. Your message has not been sent."
Actions.sendDraftError(@draftLocalId, msg)
@notifyErrorMessage(msg) @notifyErrorMessage(msg)
onOfflineError: -> onOfflineError: ->
msg = "You are offline. Your message has NOT been sent. Please send your message when you come back online." msg = "You are offline. Your message has NOT been sent. Please send your message when you come back online."
Actions.sendDraftError(@draftLocalId, msg)
@notifyErrorMessage(msg) @notifyErrorMessage(msg)