Skip to content

Commit

Permalink
[desk-tool] Only load full list when scrolled to half-way point
Browse files Browse the repository at this point in the history
  • Loading branch information
rexxars committed Dec 18, 2019
1 parent 13385dd commit a68394b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 33 deletions.
76 changes: 44 additions & 32 deletions packages/@sanity/desk-tool/src/pane/DocumentsListPane.js
Expand Up @@ -7,7 +7,7 @@ import Snackbar from 'part:@sanity/components/snackbar/default'
import Spinner from 'part:@sanity/components/loading/spinner'
import {collate, getPublishedId} from 'part:@sanity/base/util/draft-utils'
import {of, combineLatest} from 'rxjs'
import {map, tap, switchMap, filter as filterEvents} from 'rxjs/operators'
import {map, tap, filter as filterEvents} from 'rxjs/operators'
import shallowEquals from 'shallow-equals'
import settings from '../settings'
import styles from './styles/DocumentsListPane.css'
Expand Down Expand Up @@ -126,13 +126,20 @@ export default class DocumentsListPane extends React.PureComponent {
}
}

state = {queryResult: {}, sortOrder: null, layout: null, isLoadingMore: false}
state = {
queryResult: {},
sortOrder: null,
layout: null,
isLoadingMore: false,
hasFullSubscription: false
}

constructor(props) {
super()
const {filter, params} = props.options
const typeName = getTypeNameFromSingleTypeFilter(filter, params)
const settingsNamespace = settings.forNamespace(typeName)
this.atLoadingThreshold = false
this.sortOrderSetting = settingsNamespace.forKey('sortOrder')
this.layoutSetting = settingsNamespace.forKey('layout')

Expand Down Expand Up @@ -166,7 +173,7 @@ export default class DocumentsListPane extends React.PureComponent {
}

componentDidMount() {
this.setupQuery()
this.setupQuery({fullList: false})
}

componentWillUnmount() {
Expand Down Expand Up @@ -210,46 +217,50 @@ export default class DocumentsListPane extends React.PureComponent {
prevProps.options.filter !== this.props.options.filter ||
!shallowEquals(prevProps.options.params, this.props.options.params)
) {
this.setupQuery()
this.setupQuery({fullList: false})
}
}

setupQuery() {
setupQuery({fullList = false}) {
if (this.queryResults$) {
this.queryResults$.unsubscribe()
}

const params = this.props.options.params || {}
const initialQuery = this.buildListQuery({fullList: false})
const fullQuery = this.buildListQuery({fullList: true})
const query = this.buildListQuery({fullList})

// Start by querying for a _partial_ result set which we can render right away
this.queryResults$ = getQueryResults(of({query: initialQuery, params}))
.pipe(
switchMap(queryResult => {
const documents = queryResult && queryResult.result && queryResult.result.documents
const numResults = documents && documents.length
const lessThanFullPage = numResults < PARTIAL_PAGE_LIMIT

// If this is a progress event, the query failed, or we didn't get a full page worth of items,
// just pass it through!
if (!documents || lessThanFullPage) {
return of(queryResult)
}
this.queryResults$ = getQueryResults(of({query, params}))
.pipe(filterEvents(fullList ? ({result}) => result : () => true))
.subscribe(queryResult =>
this.setState({queryResult, isLoadingMore: false, hasFullSubscription: fullList})
)
}

handleScroll = (scrollTop, itemHeight) => {
const {queryResult, isLoadingMore, hasFullSubscription} = this.state
const {result} = queryResult
const documents = (result && result.documents) || []
const mightHaveMoreItems = documents.length === PARTIAL_PAGE_LIMIT
if (
this.atLoadingThreshold ||
isLoadingMore ||
hasFullSubscription ||
!result ||
!mightHaveMoreItems
) {
return
}

// We've got a partial result set, so set that to state so we can display it
this.setState({queryResult, isLoadingMore: true})
if (scrollTop >= itemHeight * (PARTIAL_PAGE_LIMIT / 2)) {
// Prevent scroll handler from firing again before setState kicks in
this.atLoadingThreshold = true
this.setState({isLoadingMore: true, hasFullSubscription: true}, () => {
// Technically still here, but is only used to guard against double-firing
this.atLoadingThreshold = false
})

// Set up a query to get the entire set of documents available
// (or enough for it not to make sense to scroll to it)
return getQueryResults(of({query: fullQuery, params})).pipe(
// Don't include events that are not "complete", since it'll
// trigger the loading state again even if we have results
filterEvents(({result}) => result)
)
})
)
.subscribe(queryResult => this.setState({queryResult, isLoadingMore: false}))
this.setupQuery({fullList: true})
}
}

buildListQuery({fullList}) {
Expand Down Expand Up @@ -318,6 +329,7 @@ export default class DocumentsListPane extends React.PureComponent {
layout={layout}
getItemKey={getDocumentKey}
renderItem={this.renderItem}
onScroll={this.handleScroll}
hasMoreItems={items.length === FULL_PAGE_LIMIT}
isLoadingMore={isLoadingMore}
/>
Expand Down
10 changes: 9 additions & 1 deletion packages/@sanity/desk-tool/src/pane/InfiniteList.js
Expand Up @@ -77,6 +77,14 @@ export default enhanceWithAvailHeight(
)
}

handleScroll = scrollTop => {
if (!this.props.onScroll) {
return
}

this.props.onScroll(scrollTop, this.state.itemSize)
}

render() {
const {layout, height, items, className, renderItem, hasMoreItems, isLoadingMore} = this.props
const {triggerUpdate, itemSize} = this.state
Expand All @@ -94,7 +102,7 @@ export default enhanceWithAvailHeight(
<VirtualList
key={layout} // forcefully re-render the whole list when layout changes
data-trigger-update-hack={triggerUpdate} // see componentWillReceiveProps above
onScroll={this.props.onScroll}
onScroll={this.handleScroll}
className={className || ''}
height={height}
itemCount={addExtraItem ? items.length + 1 : items.length}
Expand Down

0 comments on commit a68394b

Please sign in to comment.