diff --git a/exports/nylas-component-kit.coffee b/exports/nylas-component-kit.coffee
index 1dc50aedb..4bcd80e63 100644
--- a/exports/nylas-component-kit.coffee
+++ b/exports/nylas-component-kit.coffee
@@ -11,7 +11,6 @@ module.exports =
Popover: require '../src/components/popover'
Flexbox: require '../src/components/flexbox'
RetinaImg: require '../src/components/retina-img'
- EmptyState: require '../src/components/empty-state'
ListTabular: require '../src/components/list-tabular'
DraggableImg: require '../src/components/draggable-img'
MultiselectList: require '../src/components/multiselect-list'
diff --git a/internal_packages/message-list/lib/message-list.cjsx b/internal_packages/message-list/lib/message-list.cjsx
index 6b1ffeaf5..e38499899 100755
--- a/internal_packages/message-list/lib/message-list.cjsx
+++ b/internal_packages/message-list/lib/message-list.cjsx
@@ -293,6 +293,7 @@ class MessageList extends React.Component
_scrollToBottom: =>
messageWrap = React.findDOMNode(@refs.messageWrap)
+ return unless messageWrap
messageWrap.scrollTop = messageWrap.scrollHeight
_cacheScrollPos: =>
diff --git a/internal_packages/search-bar/stylesheets/search-bar.less b/internal_packages/search-bar/stylesheets/search-bar.less
index f0595515b..c9fbd0290 100644
--- a/internal_packages/search-bar/stylesheets/search-bar.less
+++ b/internal_packages/search-bar/stylesheets/search-bar.less
@@ -73,7 +73,7 @@
&.clear {
position: absolute;
- top: floor(40px - 26px)/2 - 1px;
+ top: 4px;
color: @input-cancel-color;
right: @padding-base-horizontal;
display: none;
diff --git a/static/images/empty-state/blank-bottom-left@2x.png b/internal_packages/thread-list/assets/blank-bottom-left@2x.png
similarity index 100%
rename from static/images/empty-state/blank-bottom-left@2x.png
rename to internal_packages/thread-list/assets/blank-bottom-left@2x.png
diff --git a/static/images/empty-state/blank-bottom-right@2x.png b/internal_packages/thread-list/assets/blank-bottom-right@2x.png
similarity index 100%
rename from static/images/empty-state/blank-bottom-right@2x.png
rename to internal_packages/thread-list/assets/blank-bottom-right@2x.png
diff --git a/static/images/empty-state/blank-top-left@2x.png b/internal_packages/thread-list/assets/blank-top-left@2x.png
similarity index 100%
rename from static/images/empty-state/blank-top-left@2x.png
rename to internal_packages/thread-list/assets/blank-top-left@2x.png
diff --git a/static/images/empty-state/blank-top-right@2x.png b/internal_packages/thread-list/assets/blank-top-right@2x.png
similarity index 100%
rename from static/images/empty-state/blank-top-right@2x.png
rename to internal_packages/thread-list/assets/blank-top-right@2x.png
diff --git a/internal_packages/thread-list/lib/empty-state.cjsx b/internal_packages/thread-list/lib/empty-state.cjsx
new file mode 100644
index 000000000..b3b91117e
--- /dev/null
+++ b/internal_packages/thread-list/lib/empty-state.cjsx
@@ -0,0 +1,130 @@
+_ = require 'underscore'
+React = require 'react'
+classNames = require 'classnames'
+{RetinaImg} = require 'nylas-component-kit'
+{DatabaseView,
+ NamespaceStore,
+ NylasAPI,
+ WorkspaceStore} = require 'nylas-exports'
+
+EmptyMessages = [{
+ "body":"The pessimist complains about the wind.\nThe optimist expects it to change.\nThe realist adjusts the sails."
+ "byline": "- William Arthur Ward"
+},{
+ "body":"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart."
+ "byline": "- Hellen Keller"
+},{
+ "body":"Believe you can and you're halfway there."
+ "byline": "- Theodore Roosevelt"
+},{
+ "body":"Don't judge each day by the harvest you reap but by the seeds that you plant."
+ "byline": "- Robert Louis Stevenson"
+}]
+
+class ContentGeneric extends React.Component
+ render: ->
+
+
+ {@props.messageOverride ? "No threads to display."}
+
+
+
+class ContentQuotes extends React.Component
+ @displayName = 'Quotes'
+
+ constructor: (@props) ->
+ @state = {}
+
+ componentDidMount: ->
+ # Pick a random quote using the day as a seed. I know not all months have
+ # 31 days - this is good enough to generate one quote a day at random!
+ d = new Date()
+ r = d.getDate() + d.getMonth() * 31
+ message = EmptyMessages[r % EmptyMessages.length]
+ @setState(message: message)
+
+ render: ->
+
+ {@_renderMessage()}
+
+
+
+
+
+
+ _renderMessage: ->
+ if @props.messageOverride
+ {@props.messageOverride}
+ else
+
+ {@state.message?.body}
+
+ {@state.message?.byline}
+
+
+
+
+class EmptyState extends React.Component
+ @displayName = 'EmptyState'
+ @propTypes =
+ visible: React.PropTypes.bool.isRequired
+ dataView: React.PropTypes.object
+
+ constructor: (@props) ->
+ @state =
+ layoutMode: WorkspaceStore.layoutMode()
+ syncing: false
+ active: false
+
+ componentDidMount: ->
+ @_unlisteners = []
+ @_unlisteners.push WorkspaceStore.listen(@_onChange, @)
+ @_unlisteners.push NamespaceStore.listen(@_onNamespacesChanged, @)
+ @_onNamespacesChanged()
+
+ _onNamespacesChanged: ->
+ namespace = NamespaceStore.current()
+ @_worker = NylasAPI.workerForNamespace(namespace)
+ @_workerUnlisten() if @_workerUnlisten
+ @_workerUnlisten = @_worker.listen(@_onChange, @)
+ console.log(@_worker)
+ @setState(syncing: @_worker.busy())
+
+ componentWillUnmount: ->
+ unlisten() for unlisten in @_unlisteners
+ @_workerUnlisten() if @_workerUnlisten
+
+ componentDidUpdate: ->
+ if @props.visible and not @state.active
+ @setState(active:true)
+
+ componentWillReceiveProps: (newProps) ->
+ if newProps.visible is false
+ @setState(active:false)
+
+ render: ->
+ ContentComponent = ContentGeneric
+ messageOverride = null
+
+ if @props.dataView instanceof DatabaseView
+ if @state.layoutMode is 'list'
+ ContentComponent = ContentQuotes
+ if @state.syncing
+ messageOverride = "Please wait while we prepare your mailbox."
+
+ classes = classNames
+ 'empty-state': true
+ 'visible': @props.visible
+ 'active': @state.active
+
+
+
+
+
+ _onChange: ->
+ @setState
+ layoutMode: WorkspaceStore.layoutMode()
+ syncing: @_worker.busy()
+
+
+module.exports = EmptyState
diff --git a/internal_packages/thread-list/lib/thread-list.cjsx b/internal_packages/thread-list/lib/thread-list.cjsx
index 8b5f1fc30..50c895305 100644
--- a/internal_packages/thread-list/lib/thread-list.cjsx
+++ b/internal_packages/thread-list/lib/thread-list.cjsx
@@ -14,6 +14,8 @@ ThreadListQuickActions = require './thread-list-quick-actions'
ThreadListStore = require './thread-list-store'
ThreadListIcon = require './thread-list-icon'
+EmptyState = require './empty-state'
+
class ThreadListScrollTooltip extends React.Component
@displayName: 'ThreadListScrollTooltip'
@propTypes:
@@ -128,6 +130,7 @@ class ThreadList extends React.Component
'application:reply': @_onReply
'application:reply-all': @_onReplyAll
'application:forward': @_onForward
+
@itemPropsProvider = (item) ->
className: classNames
'unread': item.isUnread()
@@ -149,6 +152,7 @@ class ThreadList extends React.Component
itemHeight={39}
className="thread-list"
scrollTooltipComponent={ThreadListScrollTooltip}
+ emptyComponent={EmptyState}
collection="thread" />
else if @state.style is 'narrow'
else
diff --git a/static/components/empty-state.less b/internal_packages/thread-list/stylesheets/empty-state.less
similarity index 83%
rename from static/components/empty-state.less
rename to internal_packages/thread-list/stylesheets/empty-state.less
index a8d70308b..45472086d 100644
--- a/static/components/empty-state.less
+++ b/internal_packages/thread-list/stylesheets/empty-state.less
@@ -13,10 +13,33 @@
overflow:hidden;
> div {
- opacity:0;
+ opacity: 0;
-webkit-transition: opacity @duration ease-out;
width:100%;
height: 100%;
+ }
+
+ // Generic Mode
+ .generic {
+ text-align: center;
+
+ .message {
+ color: @text-color-very-subtle;
+ font-size: 28px;
+ font-weight: @font-weight-blond;
+ text-align: center;
+ top:45%;
+ left:50%;
+ width:80%;
+ transform: translate(-50%, -50%);
+ position: absolute;
+ white-space: pre-line;
+ }
+ }
+
+ // Quotes Mode
+
+ .quotes {
min-width: 840px;
min-height: 650px;
position: absolute;
@@ -113,4 +136,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/components/empty-state.cjsx b/src/components/empty-state.cjsx
deleted file mode 100644
index 179a83438..000000000
--- a/src/components/empty-state.cjsx
+++ /dev/null
@@ -1,64 +0,0 @@
-_ = require 'underscore'
-React = require 'react'
-classNames = require 'classnames'
-RetinaImg = require './retina-img'
-
-EmptyMessages = [{
- "body":"The pessimist complains about the wind.\nThe optimist expects it to change.\nThe realist adjusts the sails."
- "byline": "- William Arthur Ward"
-},{
- "body":"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart."
- "byline": "- Hellen Keller"
-},{
- "body":"Believe you can and you're halfway there."
- "byline": "- Theodore Roosevelt"
-},{
- "body":"Don't judge each day by the harvest you reap but by the seeds that you plant."
- "byline": "- Robert Louis Stevenson"
-}]
-
-class EmptyState extends React.Component
- @displayName = 'EmptyState'
- @propTypes =
- visible: React.PropTypes.bool.isRequired
-
- constructor: (@props) ->
- @state =
- active: false
-
- componentDidUpdate: ->
- if @props.visible and not @state.active
- # Pick a random quote using the day as a seed. I know not all months have
- # 31 days - this is good enough to generate one quote a day at random!
- d = new Date()
- r = d.getDate() + d.getMonth() * 31
- message = EmptyMessages[r % EmptyMessages.length]
- @setState(active:true, message: message)
-
- componentWillReceiveProps: (newProps) ->
- if newProps.visible is false
- @setState(active:false)
-
- render: ->
- classes = classNames
- 'empty-state': true
- 'visible': @props.visible
- 'active': @state.active
-
-
-
-
- {@state.message?.body}
-
- {@state.message?.byline}
-
-
-
-
-
-
-
-
-
-
-module.exports = EmptyState
diff --git a/src/components/multiselect-list.cjsx b/src/components/multiselect-list.cjsx
index d0a2c07ed..d398c9bfe 100644
--- a/src/components/multiselect-list.cjsx
+++ b/src/components/multiselect-list.cjsx
@@ -2,7 +2,6 @@ _ = require 'underscore'
React = require 'react'
classNames = require 'classnames'
ListTabular = require './list-tabular'
-EmptyState = require './empty-state'
Spinner = require './spinner'
{Actions,
Utils,
@@ -37,6 +36,7 @@ class MultiselectList extends React.Component
itemPropsProvider: React.PropTypes.func.isRequired
itemHeight: React.PropTypes.number.isRequired
scrollTooltipComponent: React.PropTypes.func
+ emptyComponent: React.PropTypes.func
constructor: (@props) ->
@state = @_getStateFromStores()
@@ -107,6 +107,12 @@ class MultiselectList extends React.Component
'keyboard-cursor': @state.handler.shouldShowKeyboardCursor() and item.id is @state.keyboardCursorId
props
+ emptyElement = []
+ if @props.emptyComponent
+ emptyElement = <@props.emptyComponent
+ visible={@state.ready && @state.dataView.count() is 0}
+ dataView={@state.dataView} />
+
if @state.dataView
-
+ {emptyElement}
else
diff --git a/src/flux/nylas-sync-worker.coffee b/src/flux/nylas-sync-worker.coffee
index e3d572c20..aba68cdb8 100644
--- a/src/flux/nylas-sync-worker.coffee
+++ b/src/flux/nylas-sync-worker.coffee
@@ -33,11 +33,17 @@ class NylasSyncWorker
state: ->
@_state
+ busy: ->
+ for key, state of @_state
+ if state.busy
+ return true
+ false
+
start: ->
@_resumeTimer = setInterval(@resumeFetches, 20000)
@_connection.start()
@resumeFetches()
-
+
cleanup: ->
clearInterval(@_resumeTimer)
@_connection.end()
@@ -64,7 +70,7 @@ class NylasSyncWorker
@fetchCollectionCount(model)
@fetchCollectionPage(model, {offset: 0, limit: PAGE_SIZE})
-
+
fetchCollectionCount: (model) ->
@_api.makeRequest
path: "/n/#{@_namespaceId}/#{model}"
diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee
index eac392e34..2d5e4710b 100644
--- a/src/flux/stores/workspace-store.coffee
+++ b/src/flux/stores/workspace-store.coffee
@@ -187,8 +187,9 @@ class WorkspaceStore
# Return to the root sheet. This method triggers, allowing observers
# to update.
popToRootSheet: =>
- @_sheetStack.length = 1
- @trigger()
+ if @_sheetStack.length > 1
+ @_sheetStack.length = 1
+ @trigger()
triggerDebounced: _.debounce(( -> @trigger(@)), 1)
diff --git a/static/index.less b/static/index.less
index 875ca8b59..e11a7fac4 100644
--- a/static/index.less
+++ b/static/index.less
@@ -21,5 +21,4 @@
@import "components/scroll-region";
@import "components/spinner";
@import "components/generated-form";
-@import "components/empty-state";
@import "components/unsafe";