Created
March 9, 2015 13:43
-
-
Save gaearon/0b2200aa8128caf16377 to your computer and use it in GitHub Desktop.
PaginatedList with invalidation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var { union, find, without, clone } = require('underscore'), | |
invariant = require('react/lib/invariant'); | |
class PaginatedList { | |
constructor(ids) { | |
this._ids = ids || []; | |
this._pageCount = 0; | |
this._nextPageUrl = null; | |
this._isExpectingPage = false; | |
this._isInvalidated = false; | |
} | |
getIds() { | |
return this._ids; | |
} | |
getPageCount() { | |
return this._pageCount; | |
} | |
isExpectingPage() { | |
return this._isExpectingPage; | |
} | |
getNextPageUrl() { | |
return this._nextPageUrl; | |
} | |
isLastPage() { | |
return this._nextPageUrl === null; | |
} | |
isInvalidated() { | |
return this._isInvalidated; | |
} | |
find(predicate) { | |
return find(this._ids, predicate); | |
} | |
prepend(id) { | |
this._ids = union([id], this._ids); | |
} | |
remove(id) { | |
this._ids = without(this._ids, id); | |
} | |
contains(id) { | |
return this._ids.indexOf(id) !== -1; | |
} | |
invalidate() { | |
// Empty is as good as invalidated | |
if (this._pageCount === 0) { | |
return; | |
} | |
// TODO: We'd like to support invalidating in-flight but in order to do that | |
// `receivePage` needs to be sure whether specific response is next or invalidated page | |
// and the whole chunk of consuming code will have to be refactored. Leave it for now. | |
if (this._isExpectingPage) { | |
console.warn('Cannot invalidate while fetching page. Invalidation ignored.'); | |
return; | |
} | |
this._isInvalidated = true; | |
} | |
expectPage() { | |
invariant(!this._isExpectingPage, 'Cannot call expectPage twice without prior cancelPage or receivePage call.'); | |
this._isExpectingPage = true; | |
} | |
cancelPage() { | |
invariant(this._isExpectingPage, 'Cannot call cancelPage without prior expectPage call.'); | |
this._isExpectingPage = false; | |
this._isInvalidated = false; | |
} | |
receivePage(newIds, nextPageUrl) { | |
invariant(this._isExpectingPage, 'Cannot call receivePage without prior expectPage call.'); | |
nextPageUrl = nextPageUrl || null; | |
if (this._isInvalidated) { | |
this.receiveInvalidatedFirstPage(newIds, nextPageUrl); | |
} else { | |
this.receiveNextPage(newIds, nextPageUrl); | |
} | |
this._isExpectingPage = false; | |
this._isInvalidated = false; | |
} | |
receiveNextPage(newIds, nextPageUrl) { | |
this._ids = union(this._ids, newIds); | |
this._pageCount++; | |
this._nextPageUrl = nextPageUrl; | |
} | |
receiveInvalidatedFirstPage(firstPageIds, nextPageUrl) { | |
var isCommonId = id => this._ids.indexOf(id) > -1, | |
firstPageIdsBackwards = clone(firstPageIds).reverse(), | |
lastCommonId = find(firstPageIdsBackwards, isCommonId), | |
lastCommonIdIndex = this._ids.indexOf(lastCommonId); | |
// Only attempt to merge if there is a common item | |
if (lastCommonIdIndex > -1) { | |
this._ids = union(firstPageIds, this._ids.slice(lastCommonIdIndex + 1)); | |
} else { | |
this._ids = firstPageIds; | |
this._pageCount = 1; | |
this._nextPageUrl = nextPageUrl; | |
} | |
} | |
} | |
module.exports = PaginatedList; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment