start on scroll to status in thread

This commit is contained in:
Nolan Lawson 2018-01-28 21:57:35 -08:00
parent 37661f4c6a
commit ad443af5cd
6 changed files with 85 additions and 17 deletions

View File

@ -36,7 +36,11 @@ async function addStatuses(instanceName, timelineName, newStatuses) {
let newStatusIds = newStatuses.map(status => status.id)
let oldStatusIds = store.getForTimeline(instanceName, timelineName, 'statusIds') || []
let merged = mergeArrays(oldStatusIds, newStatusIds)
store.setForTimeline(instanceName, timelineName, { statusIds: merged })
store.setForTimeline(instanceName, timelineName, {
statusIds: merged,
// if it's a status (context) list, we need to scroll to the status in question
scrollToStatusId: timelineName.startsWith('status/') && timelineName.split('/').slice(-1)[0]
})
stop('addStatuses')
}

View File

@ -2,11 +2,12 @@
<VirtualList component="{{StatusListItem}}"
:makeProps
items="{{$statusIds}}"
on:scrollToBottom="onScrollToBottom()"
scrollToItem="{{$scrollToStatusId}}"
shown="{{$initialized}}"
footerComponent="{{LoadingFooter}}"
showFooter="{{$initialized && $runningUpdate}}"
realm="{{$currentInstance + '/' + timeline}}"
on:scrollToBottom="onScrollToBottom()"
on:initializedVisibleItems="initialize()"
/>
</div>

View File

@ -30,6 +30,9 @@
export default {
oncreate () {
this.fireScrollToBottom = throttle(() => {
this.fire('scrollToBottom')
}, SCROLL_TO_BOTTOM_DELAY)
this.observe('showFooter', showFooter => {
this.store.setForRealm({showFooter: showFooter})
})
@ -37,11 +40,12 @@
mark('set items')
this.store.setForRealm({items: items})
stop('set items')
this.fireScrollToBottom = throttle(() => {
this.fire('scrollToBottom')
}, SCROLL_TO_BOTTOM_DELAY)
})
this.observe('scrollToItem', (scrollToItem) => {
mark('scrollToItem')
this.store.setForRealm({scrollToItem: scrollToItem})
stop('scrollToItem')
})
this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => {
if (allVisibleItemsHaveHeight) {
this.fire('initializedVisibleItems')

View File

@ -38,6 +38,18 @@
scrollHeight: node.scrollHeight,
offsetHeight: node.offsetHeight
})
this.observe('scrollToItemOffset', scrollToItemOffset => {
console.log('scrollToItemOffset', scrollToItemOffset)
if (!this.get('initializedScrollTop') && scrollToItemOffset && node) {
this.set({'initializedScrollTop': true})
requestAnimationFrame(() => {
mark('set scrollToItemOffset')
console.log('forcing scroll top to ', scrollToItemOffset)
node.scrollTop = scrollToItemOffset
stop('set scrollToItemOffset')
})
}
})
}
stop('onCreate VirtualListContainer')
},
@ -91,7 +103,8 @@
},
computed: {
// TODO: bug in svelte/store the observer in oncreate() never get removed without this hack
allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight
allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight,
scrollToItemOffset: ($scrollToItemOffset) => $scrollToItemOffset
}
};
</script>

View File

@ -73,9 +73,13 @@ virtualListStore.compute('offsetHeight', ['currentRealm', 'realms'], (currentRea
return realms[currentRealm] && realms[currentRealm].offsetHeight || 0
})
virtualListStore.compute('scrollToItem', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].scrollToItem
})
virtualListStore.compute('visibleItems',
['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
(items, scrollTop, itemHeights, offsetHeight) => {
['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'itemsLeftToCalculateHeight'],
(items, scrollTop, itemHeights, offsetHeight, itemsLeftToCalculateHeight) => {
mark('compute visibleItems')
let renderBuffer = VIEWPORT_RENDER_FACTOR * offsetHeight
let visibleItems = []
@ -87,14 +91,16 @@ virtualListStore.compute('visibleItems',
let height = itemHeights[key] || 0
let currentOffset = totalOffset
totalOffset += height
let isBelowViewport = (currentOffset < scrollTop)
if (isBelowViewport) {
if (scrollTop - renderBuffer > currentOffset) {
continue // below the area we want to render
}
} else {
if (currentOffset > (scrollTop + height + renderBuffer)) {
break // above the area we want to render
if (!itemsLeftToCalculateHeight) {
let isBelowViewport = (currentOffset < scrollTop)
if (!isBelowViewport) {
if (scrollTop - renderBuffer > currentOffset) {
continue // below the area we want to render
}
} else {
if (currentOffset > (scrollTop + height + renderBuffer)) {
break // above the area we want to render
}
}
}
visibleItems.push({
@ -142,6 +148,45 @@ virtualListStore.compute('allVisibleItemsHaveHeight',
return true
})
// if we need to initialize the scroll at a particular item, then
// we effectively have to calculate all visible item heights
// TODO: technically not, we only need to calculate the items above it... or even estimate
virtualListStore.compute('mustCalculateAllItemHeights',
['scrollToItem'],
(scrollToItem) => !!scrollToItem
)
virtualListStore.compute('itemsLeftToCalculateHeight',
['mustCalculateAllItemHeights', 'itemHeights', 'items'],
(mustCalculateAllItemHeights, itemHeights, items) => {
if (!mustCalculateAllItemHeights) {
return false
}
for (let item of items) {
if (!itemHeights[item]) {
return true
}
}
return false
})
virtualListStore.compute('scrollToItemOffset',
['mustCalculateAllItemHeights', 'itemsLeftToCalculateHeight', 'scrollToItem', 'items', 'itemHeights'],
(mustCalculateAllItemHeights, itemsLeftToCalculateHeight, scrollToItem, items, itemHeights) => {
if (!mustCalculateAllItemHeights || itemsLeftToCalculateHeight) {
return null
}
let offset = 0
for (let item of items) {
if (item === scrollToItem) {
break
}
offset += itemHeights[item]
}
return offset
})
if (process.browser && process.env.NODE_ENV !== 'production') {
window.virtualListStore = virtualListStore
}

View File

@ -8,4 +8,5 @@ export function timelineComputations(store) {
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
store.compute('lastStatusId', ['statusIds'], (statusIds) => statusIds.length && statusIds[statusIds.length - 1])
store.compute('scrollToStatusId', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.scrollToStatusId)
}