[ARVADOS] updated: 2220f7f54be3445457a82c71e1e60d6492ede3a3
Git user
git at public.curoverse.com
Thu Aug 10 14:57:01 EDT 2017
Summary of changes:
.../assets/javascripts/components/collections.js | 82 +++++---------
.../app/assets/javascripts/models/loader.js | 126 +++++++++++++++++++++
2 files changed, 152 insertions(+), 56 deletions(-)
create mode 100644 apps/workbench/app/assets/javascripts/models/loader.js
via 2220f7f54be3445457a82c71e1e60d6492ede3a3 (commit)
via 2014757448a9ce52f2aa4f6af4ce2284c6858bb5 (commit)
from 473c95bec9a11808bb286528a51a4dd671e1d0bb (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 2220f7f54be3445457a82c71e1e60d6492ede3a3
Author: Tom Clegg <tom at curoverse.com>
Date: Thu Aug 10 14:12:33 2017 -0400
12033: Extract multisite loader to its own class.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at curoverse.com>
diff --git a/apps/workbench/app/assets/javascripts/components/collections.js b/apps/workbench/app/assets/javascripts/components/collections.js
index 9334bcc..527ce6c 100644
--- a/apps/workbench/app/assets/javascripts/components/collections.js
+++ b/apps/workbench/app/assets/javascripts/components/collections.js
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
window.components = window.components || {}
-window.components.collection_table_narrow = {
+window.components.collection_table = {
view: function(vnode) {
return m('table.table.table-condensed', [
m('thead', m('tr', [
@@ -13,7 +13,7 @@ window.components.collection_table_narrow = {
m('th', 'last modified'),
])),
m('tbody', [
- vnode.attrs.results.displayable.map(function(item) {
+ vnode.attrs.loader.displayable.map(function(item) {
return m('tr', [
m('td', m('a.btn.btn-xs.btn-default', {href: item.session.baseURL.replace('://', '://workbench.')+'/collections/'+item.uuid}, 'Show')),
m('td', item.uuid),
@@ -24,149 +24,42 @@ window.components.collection_table_narrow = {
]),
m('tfoot', m('tr', [
m('th[colspan=4]', m('button.btn.btn-xs', {
- className: vnode.attrs.results.loadMore ? 'btn-primary' : 'btn-default',
+ className: vnode.attrs.loader.loadMore ? 'btn-primary' : 'btn-default',
style: {
display: 'block',
width: '12em',
marginLeft: 'auto',
marginRight: 'auto',
},
- disabled: !vnode.attrs.results.loadMore,
+ disabled: !vnode.attrs.loader.loadMore,
onclick: function() {
- vnode.attrs.results.loadMore()
+ vnode.attrs.loader.loadMore()
return false
},
- }, vnode.attrs.results.loadMore ? 'Load more' : '(loading)')),
+ }, vnode.attrs.loader.loadMore ? 'Load more' : '(loading)')),
])),
])
},
}
-function Pager(loadFunc) {
- // loadFunc(filters) returns a promise for a page of results.
- var pager = this
- Object.assign(pager, {
- done: false,
- items: m.stream(),
- thresholdItem: null,
- loadNextPage: function() {
- // Get the next page, if there are any more items to get.
- if (pager.done)
- return
- var filters = pager.thresholdItem ? [
- ["modified_at", "<=", pager.thresholdItem.modified_at],
- ["uuid", "!=", pager.thresholdItem.uuid],
- ] : []
- loadFunc(filters).then(function(resp) {
- var items = pager.items() || []
- Array.prototype.push.apply(items, resp.items)
- if (resp.items.length == 0)
- pager.done = true
- else
- pager.thresholdItem = resp.items[resp.items.length-1]
- pager.items(items)
- })
- },
- })
-}
-
window.components.collection_search = {
oninit: function(vnode) {
vnode.state.sessionDB = new window.models.SessionDB()
vnode.state.searchEntered = m.stream('')
vnode.state.searchStart = m.stream('')
vnode.state.searchStart.map(function(q) {
- var sessions = vnode.state.sessionDB.loadAll()
- var cookie = (new Date()).getTime()
- // Each time searchStart() is called we replace the
- // vnode.state.results stream with a new one, and use
- // the local variable to update results in callbacks. This
- // avoids crosstalk between AJAX calls from consecutive
- // searches.
- var results = {
- // Sorted items ready to display, merged from all
- // pagers.
- displayable: [],
- pagers: {},
- loadMore: false,
- // Number of undisplayed items to keep on hand for
- // each result set. When hitting "load more", if a
- // result set already has this many additional results
- // available, we don't bother fetching a new
- // page. This is the _minimum_ number of rows that
- // will be added to results.displayable in each "load
- // more" event (except for the case where all items
- // are displayed).
- lowWaterMark: 23,
- }
- vnode.state.results = results
- m.stream.merge(Object.keys(sessions).map(function(key) {
- var pager = new Pager(function(filters) {
+ vnode.state.loader = new window.models.MultisiteLoader({
+ loadFunc: function(session, filters) {
if (q)
filters.push(['any', '@@', q+':*'])
- return vnode.state.sessionDB.request(sessions[key], 'arvados/v1/collections', {
+ return vnode.state.sessionDB.request(session, 'arvados/v1/collections', {
data: {
filters: JSON.stringify(filters),
count: 'none',
},
})
- })
- results.pagers[key] = pager
- pager.loadNextPage()
- // Resolve the stream with the session key when the
- // results arrive.
- return pager.items.map(function() { return key })
- })).map(function(keys) {
- // Top (most recent) of {bottom (oldest) entry of any
- // pager that still has more pages left to fetch}
- var cutoff
- keys.forEach(function(key) {
- var pager = results.pagers[key]
- var items = pager.items()
- if (items.length == 0 || pager.done)
- return
- var last = items[items.length-1].modified_at
- if (!cutoff || cutoff < last)
- cutoff = last
- })
- var combined = []
- keys.forEach(function(key) {
- var pager = results.pagers[key]
- pager.itemsDisplayed = 0
- pager.items().every(function(item) {
- if (cutoff && item.modified_at < cutoff)
- // Some other pagers haven't caught up to
- // this point, so don't display this item
- // or anything after it.
- return false
- item.session = sessions[key]
- combined.push(item)
- pager.itemsDisplayed++
- return true // continue
- })
- })
- results.displayable = combined.sort(function(a, b) {
- return a.modified_at < b.modified_at ? 1 : -1
- })
- // Make a new loadMore function that hits the pagers
- // (if necessary according to lowWaterMark)... or set
- // results.loadMore to false if there is nothing left
- // to fetch.
- var loadable = []
- Object.keys(results.pagers).map(function(key) {
- if (!results.pagers[key].done)
- loadable.push(results.pagers[key])
- })
- if (loadable.length == 0)
- results.loadMore = false
- else
- results.loadMore = function() {
- results.loadMore = false
- loadable.map(function(pager) {
- if (pager.items().length - pager.itemsDisplayed < results.lowWaterMark)
- pager.loadNextPage()
- })
- }
+ },
+ sessionDB: vnode.state.sessionDB,
})
})
},
@@ -195,15 +88,15 @@ window.components.collection_search = {
? m('span.label.label-xs.label-danger', 'none')
: Object.keys(sessions).sort().map(function(key) {
return [m('span.label.label-xs', {
- className: vnode.state.results.pagers[key].items() ? 'label-info' : 'label-default',
+ className: vnode.state.loader.pagers[key].items() ? 'label-info' : 'label-default',
}, key), ' ']
}),
' ',
m('a[href="/sessions"]', 'Add/remove sites'),
]),
]),
- m(window.components.collection_table_narrow, {
- results: vnode.state.results,
+ m(window.components.collection_table, {
+ loader: vnode.state.loader,
}),
])
},
diff --git a/apps/workbench/app/assets/javascripts/models/loader.js b/apps/workbench/app/assets/javascripts/models/loader.js
new file mode 100644
index 0000000..1dc7079
--- /dev/null
+++ b/apps/workbench/app/assets/javascripts/models/loader.js
@@ -0,0 +1,126 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+window.models = window.models || {}
+window.models.Pager = function(loadFunc) {
+ // loadFunc(filters) must return a promise for a page of results.
+ var pager = this
+ Object.assign(pager, {
+ done: false,
+ items: m.stream(),
+ thresholdItem: null,
+ loadNextPage: function() {
+ // Get the next page, if there are any more items to get.
+ if (pager.done)
+ return
+ var filters = pager.thresholdItem ? [
+ ["modified_at", "<=", pager.thresholdItem.modified_at],
+ ["uuid", "!=", pager.thresholdItem.uuid],
+ ] : []
+ loadFunc(filters).then(function(resp) {
+ var items = pager.items() || []
+ Array.prototype.push.apply(items, resp.items)
+ if (resp.items.length == 0)
+ pager.done = true
+ else
+ pager.thresholdItem = resp.items[resp.items.length-1]
+ pager.items(items)
+ })
+ },
+ })
+}
+
+// MultisiteLoader loads pages of results from multiple API sessions
+// and merges them into a single result set.
+//
+// The constructor implicitly starts an initial page load for each
+// session.
+//
+// new MultisiteLoader({loadFunc: function(session, filters){...},
+// sessionDB: new window.models.SessionDB()}
+//
+// At any given time, ml.loadMore will be either false (meaning a page
+// load is in progress or there are no more results to fetch) or a
+// function that starts loading more results.
+//
+// loadFunc() must retrieve results in "modified_at desc" order.
+window.models = window.models || {}
+window.models.MultisiteLoader = function(config) {
+ var loader = this
+ if (!(config.loadFunc && config.sessionDB))
+ throw new Error("MultisiteLoader constructor requires loadFunc and sessionDB")
+ Object.assign(loader, config, {
+ // Sorted items ready to display, merged from all pagers.
+ displayable: [],
+ pagers: {},
+ loadMore: false,
+ // Number of undisplayed items to keep on hand for each result
+ // set. When hitting "load more", if a result set already has
+ // this many additional results available, we don't bother
+ // fetching a new page. This is the _minimum_ number of rows
+ // that will be added to loader.displayable in each "load
+ // more" event (except for the case where all items are
+ // displayed).
+ lowWaterMark: 23,
+ })
+ var sessions = loader.sessionDB.loadAll()
+ m.stream.merge(Object.keys(sessions).map(function(key) {
+ var pager = new window.models.Pager(loader.loadFunc.bind(null, sessions[key]))
+ loader.pagers[key] = pager
+ pager.loadNextPage()
+ // Resolve the stream with the session key when the results
+ // arrive.
+ return pager.items.map(function() { return key })
+ })).map(function(keys) {
+ // Top (most recent) of {bottom (oldest) entry of any pager
+ // that still has more pages left to fetch}
+ var cutoff
+ keys.forEach(function(key) {
+ var pager = loader.pagers[key]
+ var items = pager.items()
+ if (items.length == 0 || pager.done)
+ return
+ var last = items[items.length-1].modified_at
+ if (!cutoff || cutoff < last)
+ cutoff = last
+ })
+ var combined = []
+ keys.forEach(function(key) {
+ var pager = loader.pagers[key]
+ pager.itemsDisplayed = 0
+ pager.items().every(function(item) {
+ if (cutoff && item.modified_at < cutoff)
+ // Some other pagers haven't caught up to this
+ // point, so don't display this item or anything
+ // after it.
+ return false
+ item.session = sessions[key]
+ combined.push(item)
+ pager.itemsDisplayed++
+ return true // continue
+ })
+ })
+ loader.displayable = combined.sort(function(a, b) {
+ return a.modified_at < b.modified_at ? 1 : -1
+ })
+ // Make a new loadMore function that hits the pagers (if
+ // necessary according to lowWaterMark)... or set
+ // loader.loadMore to false if there is nothing left to fetch.
+ var loadable = []
+ Object.keys(loader.pagers).map(function(key) {
+ if (!loader.pagers[key].done)
+ loadable.push(loader.pagers[key])
+ })
+ if (loadable.length == 0)
+ loader.loadMore = false
+ else
+ loader.loadMore = function() {
+ loader.loadMore = false
+ loadable.map(function(pager) {
+ if (pager.items().length - pager.itemsDisplayed < loader.lowWaterMark)
+ pager.loadNextPage()
+ })
+ }
+ })
+}
commit 2014757448a9ce52f2aa4f6af4ce2284c6858bb5
Author: Tom Clegg <tom at curoverse.com>
Date: Thu Aug 10 13:48:31 2017 -0400
12033: Add "load more" button.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at curoverse.com>
diff --git a/apps/workbench/app/assets/javascripts/components/collections.js b/apps/workbench/app/assets/javascripts/components/collections.js
index 0c821e9..9334bcc 100644
--- a/apps/workbench/app/assets/javascripts/components/collections.js
+++ b/apps/workbench/app/assets/javascripts/components/collections.js
@@ -13,7 +13,7 @@ window.components.collection_table_narrow = {
m('th', 'last modified'),
])),
m('tbody', [
- vnode.attrs.items.map(function(item) {
+ vnode.attrs.results.displayable.map(function(item) {
return m('tr', [
m('td', m('a.btn.btn-xs.btn-default', {href: item.session.baseURL.replace('://', '://workbench.')+'/collections/'+item.uuid}, 'Show')),
m('td', item.uuid),
@@ -22,6 +22,22 @@ window.components.collection_table_narrow = {
])
}),
]),
+ m('tfoot', m('tr', [
+ m('th[colspan=4]', m('button.btn.btn-xs', {
+ className: vnode.attrs.results.loadMore ? 'btn-primary' : 'btn-default',
+ style: {
+ display: 'block',
+ width: '12em',
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ disabled: !vnode.attrs.results.loadMore,
+ onclick: function() {
+ vnode.attrs.results.loadMore()
+ return false
+ },
+ }, vnode.attrs.results.loadMore ? 'Load more' : '(loading)')),
+ ])),
])
},
}
@@ -32,19 +48,22 @@ function Pager(loadFunc) {
Object.assign(pager, {
done: false,
items: m.stream(),
- lastModifiedAt: null,
+ thresholdItem: null,
loadNextPage: function() {
// Get the next page, if there are any more items to get.
if (pager.done)
return
- var filters = pager.lastModifiedAt ? [["modified_at", "<=", pager.lastModifiedAt]] : []
+ var filters = pager.thresholdItem ? [
+ ["modified_at", "<=", pager.thresholdItem.modified_at],
+ ["uuid", "!=", pager.thresholdItem.uuid],
+ ] : []
loadFunc(filters).then(function(resp) {
var items = pager.items() || []
Array.prototype.push.apply(items, resp.items)
if (resp.items.length == 0)
pager.done = true
else
- pager.lastModifiedAt = resp.items[resp.items.length-1].modified_at
+ pager.thresholdItem = resp.items[resp.items.length-1]
pager.items(items)
})
},
@@ -56,15 +75,31 @@ window.components.collection_search = {
vnode.state.sessionDB = new window.models.SessionDB()
vnode.state.searchEntered = m.stream('')
vnode.state.searchStart = m.stream('')
- // items ready to display
- vnode.state.displayItems = m.stream([])
- // {sessionKey -> Pager}
- vnode.state.pagers = {}
vnode.state.searchStart.map(function(q) {
var sessions = vnode.state.sessionDB.loadAll()
var cookie = (new Date()).getTime()
- var displayItems = m.stream([])
- vnode.state.displayItems = displayItems
+ // Each time searchStart() is called we replace the
+ // vnode.state.results stream with a new one, and use
+ // the local variable to update results in callbacks. This
+ // avoids crosstalk between AJAX calls from consecutive
+ // searches.
+ var results = {
+ // Sorted items ready to display, merged from all
+ // pagers.
+ displayable: [],
+ pagers: {},
+ loadMore: false,
+ // Number of undisplayed items to keep on hand for
+ // each result set. When hitting "load more", if a
+ // result set already has this many additional results
+ // available, we don't bother fetching a new
+ // page. This is the _minimum_ number of rows that
+ // will be added to results.displayable in each "load
+ // more" event (except for the case where all items
+ // are displayed).
+ lowWaterMark: 23,
+ }
+ vnode.state.results = results
m.stream.merge(Object.keys(sessions).map(function(key) {
var pager = new Pager(function(filters) {
if (q)
@@ -76,20 +111,62 @@ window.components.collection_search = {
},
})
})
- vnode.state.pagers[key] = pager
+ results.pagers[key] = pager
pager.loadNextPage()
+ // Resolve the stream with the session key when the
+ // results arrive.
return pager.items.map(function() { return key })
})).map(function(keys) {
+ // Top (most recent) of {bottom (oldest) entry of any
+ // pager that still has more pages left to fetch}
+ var cutoff
+ keys.forEach(function(key) {
+ var pager = results.pagers[key]
+ var items = pager.items()
+ if (items.length == 0 || pager.done)
+ return
+ var last = items[items.length-1].modified_at
+ if (!cutoff || cutoff < last)
+ cutoff = last
+ })
var combined = []
keys.forEach(function(key) {
- vnode.state.pagers[key].items().forEach(function(item) {
+ var pager = results.pagers[key]
+ pager.itemsDisplayed = 0
+ pager.items().every(function(item) {
+ if (cutoff && item.modified_at < cutoff)
+ // Some other pagers haven't caught up to
+ // this point, so don't display this item
+ // or anything after it.
+ return false
item.session = sessions[key]
combined.push(item)
+ pager.itemsDisplayed++
+ return true // continue
})
})
- displayItems(combined.sort(function(a, b) {
+ results.displayable = combined.sort(function(a, b) {
return a.modified_at < b.modified_at ? 1 : -1
- }))
+ })
+ // Make a new loadMore function that hits the pagers
+ // (if necessary according to lowWaterMark)... or set
+ // results.loadMore to false if there is nothing left
+ // to fetch.
+ var loadable = []
+ Object.keys(results.pagers).map(function(key) {
+ if (!results.pagers[key].done)
+ loadable.push(results.pagers[key])
+ })
+ if (loadable.length == 0)
+ results.loadMore = false
+ else
+ results.loadMore = function() {
+ results.loadMore = false
+ loadable.map(function(pager) {
+ if (pager.items().length - pager.itemsDisplayed < results.lowWaterMark)
+ pager.loadNextPage()
+ })
+ }
})
})
},
@@ -118,7 +195,7 @@ window.components.collection_search = {
? m('span.label.label-xs.label-danger', 'none')
: Object.keys(sessions).sort().map(function(key) {
return [m('span.label.label-xs', {
- className: vnode.state.pagers[key].items() ? 'label-info' : 'label-default',
+ className: vnode.state.results.pagers[key].items() ? 'label-info' : 'label-default',
}, key), ' ']
}),
' ',
@@ -126,7 +203,7 @@ window.components.collection_search = {
]),
]),
m(window.components.collection_table_narrow, {
- items: vnode.state.displayItems(),
+ results: vnode.state.results,
}),
])
},
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list