mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-11 10:12:00 +08:00
feat(sync): expanded details on sync status
Summary: Fixes T3574 Test Plan: manual Reviewers: dillon, bengotow Reviewed By: dillon, bengotow Maniphest Tasks: T3574 Differential Revision: https://phab.nylas.com/D2058
This commit is contained in:
parent
a37e567888
commit
d73ff30510
4 changed files with 166 additions and 45 deletions
|
@ -2,12 +2,12 @@ React = require 'react'
|
|||
_ = require 'underscore'
|
||||
classNames = require 'classnames'
|
||||
NotificationStore = require './notifications-store'
|
||||
InitialSyncActivity = require './initial-sync-activity'
|
||||
{Actions,
|
||||
TaskQueue,
|
||||
AccountStore,
|
||||
NylasSyncStatusStore,
|
||||
TaskQueueStatusStore,
|
||||
NylasAPI} = require 'nylas-exports'
|
||||
TaskQueueStatusStore} = require 'nylas-exports'
|
||||
ActivitySidebarLongPollStore = require './activity-sidebar-long-poll-store'
|
||||
{TimeoutTransitionGroup, RetinaImg} = require 'nylas-component-kit'
|
||||
|
||||
|
@ -25,8 +25,8 @@ class ActivitySidebar extends React.Component
|
|||
componentDidMount: =>
|
||||
@_unlisteners = []
|
||||
@_unlisteners.push TaskQueueStatusStore.listen @_onDataChanged
|
||||
@_unlisteners.push NylasSyncStatusStore.listen @_onDataChanged
|
||||
@_unlisteners.push NotificationStore.listen @_onDataChanged
|
||||
@_unlisteners.push NylasSyncStatusStore.listen @_onDataChanged
|
||||
@_unlisteners.push ActivitySidebarLongPollStore.listen @_onDeltaReceived
|
||||
|
||||
componentWillUnmount: =>
|
||||
|
@ -34,10 +34,14 @@ class ActivitySidebar extends React.Component
|
|||
@_workerUnlisten() if @_workerUnlisten
|
||||
|
||||
render: =>
|
||||
items = [].concat(@_renderSyncActivityItem(), @_renderNotificationActivityItems(), @_renderTaskActivityItems())
|
||||
items = [@_renderNotificationActivityItems(), @_renderTaskActivityItems()]
|
||||
|
||||
if @state.isInitialSyncComplete
|
||||
if @state.receivingDelta
|
||||
items.push @_renderDeltaSyncActivityItem()
|
||||
else
|
||||
items.push <InitialSyncActivity />
|
||||
|
||||
if @state.receivingDelta
|
||||
items.push @_renderDeltaSyncActivityItem()
|
||||
|
||||
names = classNames
|
||||
"sidebar-activity": true
|
||||
|
@ -64,39 +68,6 @@ class ActivitySidebar extends React.Component
|
|||
{inside}
|
||||
</TimeoutTransitionGroup>
|
||||
|
||||
_renderSyncActivityItem: =>
|
||||
count = 0
|
||||
fetched = 0
|
||||
progress = 0
|
||||
incomplete = 0
|
||||
error = null
|
||||
|
||||
for acctId, state of @state.sync
|
||||
for model, modelState of state
|
||||
incomplete += 1 unless modelState.complete
|
||||
error ?= modelState.error
|
||||
if modelState.count
|
||||
count += modelState.count / 1
|
||||
fetched += modelState.fetched / 1
|
||||
|
||||
progress = (fetched / count) * 100 if count > 0
|
||||
|
||||
if incomplete is 0
|
||||
return []
|
||||
else if error
|
||||
<div className="item" key="initial-sync">
|
||||
<div className="inner">Initial sync encountered an error. Waiting to retry...
|
||||
<div className="btn btn-emphasis" onClick={@_onTryAgain}>Try Again</div>
|
||||
</div>
|
||||
</div>
|
||||
else
|
||||
<div className="item" key="initial-sync">
|
||||
<div className="progress-track">
|
||||
<div className="progress" style={width: "#{progress}%"}></div>
|
||||
</div>
|
||||
<div className="inner">Syncing mail data…</div>
|
||||
</div>
|
||||
|
||||
_renderTaskActivityItems: =>
|
||||
summary = {}
|
||||
|
||||
|
@ -131,16 +102,13 @@ class ActivitySidebar extends React.Component
|
|||
</div>
|
||||
</div>
|
||||
|
||||
_onTryAgain: =>
|
||||
Actions.retryInitialSync()
|
||||
|
||||
_onDataChanged: =>
|
||||
@setState(@_getStateFromStores())
|
||||
|
||||
_getStateFromStores: =>
|
||||
notifications: NotificationStore.notifications()
|
||||
tasks: TaskQueueStatusStore.queue()
|
||||
sync: NylasSyncStatusStore.state()
|
||||
isInitialSyncComplete: NylasSyncStatusStore.isComplete()
|
||||
|
||||
_onDeltaReceived: (countDeltas) =>
|
||||
tooSmallForNotification = countDeltas <= 10
|
||||
|
|
109
internal_packages/notifications/lib/initial-sync-activity.cjsx
Normal file
109
internal_packages/notifications/lib/initial-sync-activity.cjsx
Normal file
|
@ -0,0 +1,109 @@
|
|||
_ = require 'underscore'
|
||||
_str = require 'underscore.string'
|
||||
classNames = require 'classnames'
|
||||
React = require 'react'
|
||||
{Actions, AccountStore, NylasSyncStatusStore} = require 'nylas-exports'
|
||||
|
||||
class InitialSyncActivity extends React.Component
|
||||
@displayName: 'InitialSyncActivity'
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = @_getStateFromStores()
|
||||
|
||||
componentDidMount: =>
|
||||
@_usub = NylasSyncStatusStore.listen @_onDataChanged
|
||||
|
||||
componentWillUnmount: =>
|
||||
@_usub?()
|
||||
|
||||
_onDataChanged: =>
|
||||
@setState(@_getStateFromStores())
|
||||
|
||||
_getStateFromStores: =>
|
||||
sync: NylasSyncStatusStore.state()
|
||||
|
||||
render: =>
|
||||
count = 0
|
||||
fetched = 0
|
||||
totalProgress = 0
|
||||
incomplete = 0
|
||||
error = null
|
||||
|
||||
for acctId, state of @state.sync
|
||||
for model, modelState of state
|
||||
incomplete += 1 unless modelState.complete
|
||||
error ?= modelState.error
|
||||
if modelState.count
|
||||
count += modelState.count / 1
|
||||
fetched += modelState.fetched / 1
|
||||
|
||||
totalProgress = (fetched / count) * 100 if count > 0
|
||||
|
||||
classSet = classNames
|
||||
'item': true
|
||||
'expanded-sync': @state.expandedSync
|
||||
|
||||
if incomplete is 0
|
||||
return false
|
||||
else if error
|
||||
<div className={classSet} key="initial-sync">
|
||||
<div className="inner">Initial sync encountered an error. Waiting to retry...
|
||||
<div className="btn btn-emphasis" onClick={@_onTryAgain}>Try Again</div>
|
||||
</div>
|
||||
{@_expandedSyncState()}
|
||||
</div>
|
||||
else
|
||||
<div className={classSet} key="initial-sync" onClick={=> @setState expandedSync: !@state.expandedSync}>
|
||||
{@_renderProgressBar(totalProgress)}
|
||||
<div className="inner">Syncing mail data…</div>
|
||||
{@_expandedSyncState()}
|
||||
</div>
|
||||
|
||||
_expandedSyncState: ->
|
||||
accounts = []
|
||||
for acctId, state of @state.sync
|
||||
account = _.findWhere(AccountStore.items(), id: acctId)
|
||||
continue unless account
|
||||
modelStates = _.map state, (modelState, model) =>
|
||||
@_renderModelProgress(model, modelState, 100)
|
||||
|
||||
accounts.push <div className="account inner" key={acctId}>
|
||||
<h2>{account.emailAddress}</h2>
|
||||
{modelStates}
|
||||
</div>
|
||||
accounts.push <a className="close-expanded" onClick={@_hideExpandedState}>Hide</a>
|
||||
<div className="account-detail-area">
|
||||
{accounts}
|
||||
</div>
|
||||
|
||||
_hideExpandedState: (event) =>
|
||||
event.stopPropagation() # So it doesn't reach the parent's onClick
|
||||
event.preventDefault()
|
||||
@setState expandedSync: false
|
||||
return
|
||||
|
||||
_renderModelProgress: (model, modelState) ->
|
||||
if modelState.error
|
||||
status = "error"
|
||||
else if modelState.complete
|
||||
status = "complete"
|
||||
else
|
||||
status = "busy"
|
||||
percent = (+modelState.fetched / +modelState.count) * 100
|
||||
|
||||
<div className="model-progress #{status}" key={model}>
|
||||
<h3>{_str.titleize(model)}:</h3>
|
||||
{@_renderProgressBar(percent)}
|
||||
<div className="amount">{_str.numberFormat(modelState.fetched)} / {_str.numberFormat(modelState.count)}</div>
|
||||
<div className="error-text">{modelState.error}</div>
|
||||
</div>
|
||||
|
||||
_renderProgressBar: (percent) ->
|
||||
<div className="progress-track">
|
||||
<div className="progress" style={width: "#{percent}%"}></div>
|
||||
</div>
|
||||
|
||||
_onTryAgain: =>
|
||||
Actions.retryInitialSync()
|
||||
|
||||
module.exports = InitialSyncActivity
|
|
@ -19,16 +19,17 @@
|
|||
font-size: @font-size-small;
|
||||
color: @text-color-subtle;
|
||||
line-height:@line-height-computed * 0.95;
|
||||
height:140px;
|
||||
overflow-y:scroll;
|
||||
box-shadow:inset 0 1px 0 @border-color-divider;
|
||||
&:hover { cursor: default }
|
||||
|
||||
.item {
|
||||
border-bottom:1px solid @border-color-divider;
|
||||
&:hover { cursor: default }
|
||||
|
||||
.inner {
|
||||
padding: @padding-large-vertical @padding-base-horizontal @padding-large-vertical @padding-base-horizontal;
|
||||
margin-top:3px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
.count {
|
||||
color: @text-color-very-subtle;
|
||||
|
@ -45,6 +46,7 @@
|
|||
display:block;
|
||||
height:3px;
|
||||
font-size:0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
.progress {
|
||||
transition: width 0.4s;
|
||||
height:3px;
|
||||
|
@ -55,6 +57,42 @@
|
|||
// properly. Removing position relative causes the div to remain visible
|
||||
position:relative;
|
||||
opacity: 1;
|
||||
|
||||
.account-detail-area {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s;
|
||||
}
|
||||
&.expanded-sync {
|
||||
.account-detail-area {
|
||||
max-height: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.account.inner {
|
||||
border-top: 1px solid rgba(0,0,0,0.1)
|
||||
}
|
||||
.account h2 {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
}
|
||||
.account h3 {
|
||||
font-size: 14px;
|
||||
margin: 1em 0 4px 0;
|
||||
}
|
||||
.acccount .amount {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.complete .progress-track .progress {
|
||||
background-color: @nylas-green;
|
||||
}
|
||||
.close-expanded {
|
||||
display: block;
|
||||
margin: 0 @padding-base-horizontal @padding-large-vertical @padding-base-horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
transition: height 0.4s;
|
||||
|
|
|
@ -30,6 +30,12 @@ class NylasSyncStatusStore extends NylasStore
|
|||
state: =>
|
||||
@_statesByAccount
|
||||
|
||||
isComplete: ->
|
||||
for acctId, state of @_statesByAccount
|
||||
for model, modelState of state
|
||||
return false if not modelState.complete
|
||||
return true
|
||||
|
||||
busy: =>
|
||||
for accountId, states of @_statesByAccount
|
||||
for key, state of states
|
||||
|
|
Loading…
Reference in a new issue