Implement ajax polling for things that are visible on the screen
We sometimes miss pusher updates, which started to be more common lately. Until we investigate what's going on, this should be a good workaround for keeping UI in sync with the DB.
This commit is contained in:
parent
a304ce2640
commit
a4a75912b0
8
app/components/running-jobs-item.coffee
Normal file
8
app/components/running-jobs-item.coffee
Normal file
|
@ -0,0 +1,8 @@
|
|||
`import Ember from 'ember'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
RunningJobsItemComponent = Ember.Component.extend(Polling,
|
||||
pollModels: 'job'
|
||||
)
|
||||
|
||||
`export default RunningJobsItemComponent`
|
|
@ -12,7 +12,8 @@ Controller = Ember.ArrayController.extend
|
|||
|
||||
isLoaded: false
|
||||
content: (->
|
||||
result = @store.filter('job', { state: 'started' }, (job) ->
|
||||
# TODO: this should also query for received jobs
|
||||
result = @store.filter('job', {}, (job) ->
|
||||
['started', 'received'].indexOf(job.get('state')) != -1
|
||||
)
|
||||
result.then =>
|
||||
|
|
63
app/mixins/polling.coffee
Normal file
63
app/mixins/polling.coffee
Normal file
|
@ -0,0 +1,63 @@
|
|||
`import Ember from 'ember'`
|
||||
|
||||
mixin = Ember.Mixin.create
|
||||
polling: Ember.inject.service()
|
||||
|
||||
didInsertElement: ->
|
||||
@_super.apply(this, arguments)
|
||||
|
||||
@startPolling()
|
||||
|
||||
willDestroyElement: ->
|
||||
@_super.apply(this, arguments)
|
||||
|
||||
@stopPolling()
|
||||
|
||||
willDestroy: ->
|
||||
@_super.apply(this, arguments)
|
||||
|
||||
@stopPolling()
|
||||
|
||||
pollModelDidChange: (sender, key, value) ->
|
||||
@pollModel(key)
|
||||
|
||||
pollModelWillChange: (sender, key, value) ->
|
||||
@stopPollingModel(key)
|
||||
|
||||
pollModel: (property) ->
|
||||
model = @get(property)
|
||||
|
||||
@get('polling').startPolling(model)
|
||||
|
||||
stopPollingModel: (property) ->
|
||||
model = @get(property)
|
||||
|
||||
@get('polling').stopPolling(model)
|
||||
|
||||
startPolling: ->
|
||||
pollModels = @get('pollModels')
|
||||
|
||||
if pollModels
|
||||
pollModels = [pollModels] unless pollModels.forEeach
|
||||
|
||||
pollModels.forEach (property) =>
|
||||
@pollModel(property)
|
||||
@addObserver(property, this, 'pollModelDidChange')
|
||||
Ember.addBeforeObserver(this, property, this, 'pollModelWillChange')
|
||||
|
||||
@get('polling').startPollingHook(this) if @pollHook
|
||||
|
||||
stopPolling: ->
|
||||
pollModels = @get('pollModels')
|
||||
return unless pollModels
|
||||
|
||||
pollModels = [pollModels] unless pollModels.forEeach
|
||||
|
||||
pollModels.forEach (property) =>
|
||||
@stopPollingModel(property)
|
||||
@removeObserver(property, this, 'pollModelDidChange')
|
||||
Ember.removeBeforeObserver(this, property, this, 'pollModelWillChange')
|
||||
|
||||
@get('polling').stopPollingHook(this)
|
||||
|
||||
`export default mixin`
|
|
@ -11,6 +11,7 @@ Route = TravisRoute.extend
|
|||
@controllerFor('repo').activate(@get('contentType'))
|
||||
@contentDidChange()
|
||||
@controllerFor('repo').addObserver(@get('path'), this, 'contentDidChange')
|
||||
@controllerFor('build').set('contentType', @get('contentType'))
|
||||
|
||||
deactivate: ->
|
||||
@controllerFor('repo').removeObserver(@get('path'), this, 'contentDidChange')
|
||||
|
|
49
app/services/polling.coffee
Normal file
49
app/services/polling.coffee
Normal file
|
@ -0,0 +1,49 @@
|
|||
`import Ember from 'ember'`
|
||||
|
||||
service = Ember.Object.extend
|
||||
init: ->
|
||||
@_super.apply(this, arguments)
|
||||
|
||||
@set('watchedModels', [])
|
||||
@set('sources', [])
|
||||
|
||||
interval = setInterval =>
|
||||
@poll()
|
||||
, 30000
|
||||
|
||||
@set('interval', interval)
|
||||
|
||||
willDestroy: ->
|
||||
@_super.apply(this, arguments)
|
||||
|
||||
clearInterval(@get('interval'))
|
||||
|
||||
startPollingHook: (source) ->
|
||||
sources = @get('sources')
|
||||
unless sources.contains(source)
|
||||
sources.pushObject(source)
|
||||
|
||||
stopPollingHook: (source) ->
|
||||
sources = @get('sources')
|
||||
sources.removeObject(source)
|
||||
|
||||
startPolling: (model) ->
|
||||
watchedModels = @get('watchedModels')
|
||||
unless watchedModels.contains(model)
|
||||
watchedModels.pushObject(model)
|
||||
|
||||
stopPolling: (model) ->
|
||||
watchedModels = @get('watchedModels')
|
||||
watchedModels.removeObject(model)
|
||||
|
||||
poll: ->
|
||||
@get('watchedModels').forEach (model) ->
|
||||
model.reload()
|
||||
|
||||
@get('sources').forEach (source) =>
|
||||
if source.get('destroyed')
|
||||
@get('sources').removeObject(source)
|
||||
else
|
||||
source.pollHook()
|
||||
|
||||
`export default service`
|
1
app/templates/components/running-jobs-item.hbs
Normal file
1
app/templates/components/running-jobs-item.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{yield}}
|
|
@ -1,29 +1,7 @@
|
|||
{{#if isLoaded}}
|
||||
{{#if controller.length}}
|
||||
{{#each job in controller}}
|
||||
<div {{bind-attr class=":tile :tile--sidebar job.state"}}>
|
||||
{{#if job.repo.slug}}
|
||||
<span {{bind-attr class=":icon :icon--job job.state"}}></span>
|
||||
{{#link-to "job" job.repo job}}{{job.repo.slug}}{{/link-to}}
|
||||
{{/if}}
|
||||
|
||||
<p class="tile-title float-right">
|
||||
<span class="icon icon--hash"></span>
|
||||
{{#if job.repo.slug}}
|
||||
{{#link-to "job" job.repo job}}{{job.number}}{{/link-to}}
|
||||
{{/if}}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span class="icon icon--clock"></span>
|
||||
Duration:
|
||||
<abbr class="duration" {{bind-attr title="job.startedAt"}}>
|
||||
{{format-duration job.duration}}
|
||||
</abbr>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
{{running-jobs-item job=job}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div class="spinner-container">There are no jobs running</div>
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
`import { colorForState } from 'travis/utils/helpers'`
|
||||
`import BasicView from 'travis/views/basic'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
View = BasicView.extend
|
||||
View = BasicView.extend Polling,
|
||||
classNameBindings: ['color']
|
||||
buildBinding: 'controller.build'
|
||||
|
||||
pollModels: 'controller.build'
|
||||
|
||||
color: (->
|
||||
colorForState(@get('build.state'))
|
||||
).property('build.state')
|
||||
|
|
18
app/views/builds.coffee
Normal file
18
app/views/builds.coffee
Normal file
|
@ -0,0 +1,18 @@
|
|||
`import BasicView from 'travis/views/basic'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
View = BasicView.extend Polling,
|
||||
pollHook: (store) ->
|
||||
contentType = @get('controller.contentType')
|
||||
repositoryId = @get('controller.repo.id')
|
||||
store = @get('controller.store')
|
||||
|
||||
if contentType == 'builds'
|
||||
store.find('build', { event_type: 'push', repository_id: repositoryId })
|
||||
else if contentType == 'pull_requests'
|
||||
store.filter('build', { event_type: 'pull_request', repository_id: repositoryId })
|
||||
else
|
||||
store.find 'build', repository_id: repositoryId, branches: true
|
||||
|
||||
|
||||
`export default View`
|
|
@ -1,8 +1,11 @@
|
|||
`import Ember from 'ember'`
|
||||
`import { colorForState } from 'travis/utils/helpers'`
|
||||
`import { githubCommit, gravatarImage } from 'travis/utils/urls'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
View = Ember.View.extend Polling,
|
||||
pollModels: 'controller.job'
|
||||
|
||||
View = Ember.View.extend
|
||||
repoBinding: 'controller.repo'
|
||||
jobBinding: 'controller.job'
|
||||
commitBinding: 'job.commit'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
`import Ember from 'ember'`
|
||||
`import { colorForState } from 'travis/utils/helpers'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
View = Ember.CollectionView.extend
|
||||
elementId: ''
|
||||
|
@ -8,7 +9,9 @@ View = Ember.CollectionView.extend
|
|||
emptyView: Ember.View.extend
|
||||
templateName: 'repos-list/empty'
|
||||
|
||||
itemViewClass: Ember.View.extend
|
||||
itemViewClass: Ember.View.extend Polling,
|
||||
pollModels: 'repo'
|
||||
|
||||
repoBinding: 'content'
|
||||
classNames: ['repo']
|
||||
classNameBindings: ['color', 'selected']
|
||||
|
|
8
app/views/running-jobs.coffee
Normal file
8
app/views/running-jobs.coffee
Normal file
|
@ -0,0 +1,8 @@
|
|||
`import BasicView from 'travis/views/basic'`
|
||||
`import Polling from 'travis/mixins/polling'`
|
||||
|
||||
View = BasicView.extend Polling,
|
||||
pollHook: (store) ->
|
||||
@get('controller.store').find('job', {})
|
||||
|
||||
`export default View`
|
17
tests/unit/components/running-jobs-item-test.coffee
Normal file
17
tests/unit/components/running-jobs-item-test.coffee
Normal file
|
@ -0,0 +1,17 @@
|
|||
`import { test, moduleForComponent } from 'ember-qunit'`
|
||||
|
||||
moduleForComponent 'running-jobs-item', {
|
||||
# specify the other units that are required for this test
|
||||
# needs: ['component:foo', 'helper:bar']
|
||||
}
|
||||
|
||||
test 'it renders', (assert) ->
|
||||
assert.expect 2
|
||||
|
||||
# creates the component instance
|
||||
component = @subject()
|
||||
assert.equal component._state, 'preRender'
|
||||
|
||||
# renders the component to the page
|
||||
@render()
|
||||
assert.equal component._state, 'inDOM'
|
Loading…
Reference in New Issue
Block a user