1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 window.models = window.models || {}
6 window.models.Pager = function(loadFunc) {
7 // loadFunc(filters) must return a promise for a page of results.
13 loadNextPage: function() {
14 // Get the next page, if there are any more items to get.
17 var filters = pager.thresholdItem ? [
18 ["modified_at", "<=", pager.thresholdItem.modified_at],
19 ["uuid", "!=", pager.thresholdItem.uuid],
21 loadFunc(filters).then(function(resp) {
22 var items = pager.items() || []
23 Array.prototype.push.apply(items, resp.items)
24 if (resp.items.length == 0)
27 pager.thresholdItem = resp.items[resp.items.length-1]
34 // MultisiteLoader loads pages of results from multiple API sessions
35 // and merges them into a single result set.
37 // The constructor implicitly starts an initial page load for each
40 // new MultisiteLoader({loadFunc: function(session, filters){...},
41 // sessionDB: new window.models.SessionDB()}
43 // At any given time, ml.loadMore will be either false (meaning a page
44 // load is in progress or there are no more results to fetch) or a
45 // function that starts loading more results.
47 // loadFunc() must retrieve results in "modified_at desc" order.
48 window.models = window.models || {}
49 window.models.MultisiteLoader = function(config) {
51 if (!(config.loadFunc && config.sessionDB))
52 throw new Error("MultisiteLoader constructor requires loadFunc and sessionDB")
53 Object.assign(loader, config, {
54 // Sorted items ready to display, merged from all pagers.
58 // Number of undisplayed items to keep on hand for each result
59 // set. When hitting "load more", if a result set already has
60 // this many additional results available, we don't bother
61 // fetching a new page. This is the _minimum_ number of rows
62 // that will be added to loader.displayable in each "load
63 // more" event (except for the case where all items are
67 var sessions = loader.sessionDB.loadAll()
68 m.stream.merge(Object.keys(sessions).map(function(key) {
69 var pager = new window.models.Pager(loader.loadFunc.bind(null, sessions[key]))
70 loader.pagers[key] = pager
72 // Resolve the stream with the session key when the results
74 return pager.items.map(function() { return key })
75 })).map(function(keys) {
76 // Top (most recent) of {bottom (oldest) entry of any pager
77 // that still has more pages left to fetch}
79 keys.forEach(function(key) {
80 var pager = loader.pagers[key]
81 var items = pager.items()
82 if (items.length == 0 || pager.done)
84 var last = items[items.length-1].modified_at
85 if (!cutoff || cutoff < last)
89 keys.forEach(function(key) {
90 var pager = loader.pagers[key]
91 pager.itemsDisplayed = 0
92 pager.items().every(function(item) {
93 if (cutoff && item.modified_at < cutoff)
94 // Some other pagers haven't caught up to this
95 // point, so don't display this item or anything
98 item.session = sessions[key]
100 pager.itemsDisplayed++
101 return true // continue
104 loader.displayable = combined.sort(function(a, b) {
105 return a.modified_at < b.modified_at ? 1 : -1
107 // Make a new loadMore function that hits the pagers (if
108 // necessary according to lowWaterMark)... or set
109 // loader.loadMore to false if there is nothing left to fetch.
111 Object.keys(loader.pagers).map(function(key) {
112 if (!loader.pagers[key].done)
113 loadable.push(loader.pagers[key])
115 if (loadable.length == 0)
116 loader.loadMore = false
118 loader.loadMore = function() {
119 loader.loadMore = false
120 loadable.map(function(pager) {
121 if (pager.items().length - pager.itemsDisplayed < loader.lowWaterMark)