fix(bodies): Move loading into component, add retry, loading spinner

This commit is contained in:
Ben Gotow 2016-03-09 18:01:13 -08:00
parent a53f2ade75
commit 7a31da9fc9
4 changed files with 58 additions and 43 deletions

View file

@ -2,11 +2,12 @@ React = require 'react'
_ = require 'underscore'
EmailFrame = require './email-frame'
{Utils,
NylasAPI,
MessageUtils,
MessageBodyProcessor,
QuotedHTMLTransformer,
FileDownloadStore} = require 'nylas-exports'
{InjectedComponentSet} = require 'nylas-component-kit'
{InjectedComponentSet, RetinaImg} = require 'nylas-component-kit'
TransparentPixel = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNikAQAACIAHF/uBd8AAAAASUVORK5CYII="
@ -17,19 +18,25 @@ class MessageItemBody extends React.Component
downloads: React.PropTypes.object.isRequired
constructor: (@props) ->
@_unmounted = false
@state =
showQuotedText: Utils.isForwardedMessage(@props.message)
processedBody: undefined
processedBody: null
error: null
componentWillMount: =>
@_unsub = MessageBodyProcessor.processAndSubscribe(@props.message, @_onBodyChanged)
@_unsub = MessageBodyProcessor.subscribe(@props.message, @_onBodyProcessed)
componentDidMount: =>
@_onFetchBody() if not _.isString(@props.message.body)
componentWillReceiveProps: (nextProps) ->
if nextProps.message.id isnt @props.message.id
@_unsub?()
@_unsub = MessageBodyProcessor.processAndSubscribe(nextProps.message, @_onBodyChanged)
@_unsub = MessageBodyProcessor.subscribe(nextProps.message, @_onBodyProcessed)
componentWillUnmount: =>
@_unmounted = true
@_unsub?()
render: =>
@ -44,8 +51,20 @@ class MessageItemBody extends React.Component
</span>
_renderBody: =>
return null unless @state.processedBody?
<EmailFrame showQuotedText={@state.showQuotedText} content={@state.processedBody}/>
if _.isString(@props.message.body) and @state.processedBody
<EmailFrame showQuotedText={@state.showQuotedText} content={@state.processedBody}/>
else if @state.error
<div className="message-body-error">
Sorry, this message could not be loaded. (Status code {@state.error.statusCode})
<a onClick={@_onFetchBody}>Try Again</a>
</div>
else
<div className="message-body-loading">
<RetinaImg
name="inline-loading-spinner.gif"
mode={RetinaImg.Mode.ContentDark}
style={{width: 14, height: 14}}/>
</div>
_renderQuotedTextControl: =>
return null unless QuotedHTMLTransformer.hasQuotedHTML(@props.message.body)
@ -58,7 +77,21 @@ class MessageItemBody extends React.Component
@setState
showQuotedText: !@state.showQuotedText
_onBodyChanged: (body) =>
_onFetchBody: =>
NylasAPI.makeRequest
path: "/messages/#{@props.message.id}"
accountId: @props.message.accountId
returnsModel: true
.then =>
return if @_unmounted
@setState({error: null})
# message will be put into the database and the MessageBodyProcessor
# will provide us with the new body once it's been processed.
.catch (error) =>
return if @_unmounted
@setState({error})
_onBodyProcessed: (body) =>
downloadingSpinnerPath = Utils.imageNamed('inline-loading-spinner.gif')
# Replace cid:// references with the paths to downloaded files

View file

@ -150,6 +150,23 @@ body.platform-win32 {
a { float: right; }
}
.message-body-error {
background-color: @background-secondary;
border: 1px solid darken(@background-secondary, 8%);
color: @text-color-very-subtle;
margin-top: @padding-large-vertical;
cursor: default;
padding: @padding-base-vertical @padding-base-horizontal;
a { float: right; }
}
.message-body-loading {
height: 1em;
align-content: center;
margin-top: @padding-large-vertical;
margin-bottom: @padding-large-vertical;
}
.message-subject-wrap {
width: calc(~"100% - 12px");
max-width: @message-max-width;

View file

@ -135,7 +135,6 @@ describe "MessageStore", ->
describe "when toggling expansion of all messages", ->
beforeEach ->
MessageStore._items = [testMessage1, testMessage2, testMessage3]
spyOn(MessageStore, '_fetchExpandedBodies')
spyOn(MessageStore, '_fetchExpandedAttachments')
it 'should expand all when at default state', ->

View file

@ -4,7 +4,6 @@ Message = require "../models/message"
Thread = require "../models/thread"
Utils = require '../models/utils'
DatabaseStore = require "./database-store"
AccountStore = require "./account-store"
FocusedPerspectiveStore = require './focused-perspective-store'
FocusedContentStore = require "./focused-content-store"
ChangeUnreadTask = require '../tasks/change-unread-task'
@ -96,7 +95,6 @@ class MessageStore extends NylasStore
@_itemsLoading = false
@_showingHiddenItems = false
@_thread = null
@_inflight = {}
_registerListeners: ->
@listenTo ExtensionRegistry.MessageView, @_onExtensionsChanged
@ -212,7 +210,6 @@ class MessageStore extends NylasStore
_onToggleHiddenMessages: =>
@_showingHiddenItems = !@_showingHiddenItems
@_expandItemsToDefault()
@_fetchExpandedBodies(@_items)
@_fetchExpandedAttachments(@_items)
@trigger()
@ -228,7 +225,6 @@ class MessageStore extends NylasStore
_expandItem: (item) =>
@_itemsExpanded[item.id] = "explicit"
@_fetchExpandedBodies([item])
@_fetchExpandedAttachments([item])
_collapseItem: (item) =>
@ -262,12 +258,6 @@ class MessageStore extends NylasStore
# Download the attachments on expanded messages.
@_fetchExpandedAttachments(@_items)
# Check that expanded messages have bodies. We won't mark ourselves
# as loaded until they're all available. Note that items can be manually
# expanded so this logic must be separate from above.
if @_fetchExpandedBodies(@_items)
loaded = false
# Normally, we would trigger often and let the view's
# shouldComponentUpdate decide whether to re-render, but if we
# know we're not ready, don't even bother. Trigger once at start
@ -278,15 +268,6 @@ class MessageStore extends NylasStore
@_markAsRead()
@trigger(@)
_fetchExpandedBodies: (items) ->
startedAFetch = false
for item in items
continue unless @_itemsExpanded[item.id]
if not _.isString(item.body)
@_fetchMessageIdFromAPI(item.id)
startedAFetch = true
startedAFetch
_fetchExpandedAttachments: (items) ->
policy = NylasEnv.config.get('core.attachments.downloadPolicy')
return if policy is 'manually'
@ -304,22 +285,7 @@ class MessageStore extends NylasStore
@_itemsExpanded[item.id] = "default"
_fetchMessages: ->
account = AccountStore.accountForId(@_thread.accountId)
NylasAPI.getCollection account.id, 'messages', {thread_id: @_thread.id}
_fetchMessageIdFromAPI: (id) ->
return if @_inflight[id]
@_inflight[id] = true
account = AccountStore.accountForId(@_thread.accountId)
NylasAPI.makeRequest
path: "/messages/#{id}"
accountId: account.id
returnsModel: true
success: =>
delete @_inflight[id]
error: =>
delete @_inflight[id]
NylasAPI.getCollection(@_thread.accountId, 'messages', {thread_id: @_thread.id})
_sortItemsForDisplay: (items) ->
# Re-sort items in the list so that drafts appear after the message that