refactor virtual store to use realms

This commit is contained in:
Nolan Lawson 2018-01-27 08:13:28 -08:00
parent 610e54469e
commit 687cc5c961
11 changed files with 49 additions and 75 deletions

View file

@ -1,7 +1,7 @@
<Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/> <Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/>
{{#if virtual}} {{#if virtual}}
<VirtualListContainer storeKey="{{virtualStoreKey}}"> <VirtualListContainer realm="{{virtualRealm}}">
<main> <main>
<slot></slot> <slot></slot>
</main> </main>

View file

@ -7,7 +7,7 @@
shown="{{initialized}}" shown="{{initialized}}"
footerComponent="{{LoadingFooter}}" footerComponent="{{LoadingFooter}}"
showFooter="{{initialized && runningUpdate}}" showFooter="{{initialized && runningUpdate}}"
storeKey="{{timeline}}" realm="{{timeline}}"
on:initializedVisibleItems="initialize()" on:initializedVisibleItems="initialize()"
/> />
</div> </div>

View file

@ -31,13 +31,11 @@
export default { export default {
oncreate () { oncreate () {
this.observe('showFooter', showFooter => { this.observe('showFooter', showFooter => {
this.store.set({showFooter: showFooter}) this.store.setForRealm({showFooter: showFooter})
}) })
this.observe('items', (items) => { this.observe('items', (items) => {
mark('set items') mark('set items')
this.store.set({ this.store.setForRealm({items: items})
items: items
})
stop('set items') stop('set items')
this.fireScrollToBottom = throttle(() => { this.fireScrollToBottom = throttle(() => {
this.fire('scrollToBottom') this.fire('scrollToBottom')

View file

@ -13,73 +13,38 @@
const SCROLL_EVENT_DELAY = 300 const SCROLL_EVENT_DELAY = 300
const cachedVirtualStores = {}
if (process.browser && process.env.NODE_ENV !== 'production') {
window.cachedVirtualStores = cachedVirtualStores
}
export default { export default {
oncreate() { oncreate() {
mark('onCreate VirtualListContainer') mark('onCreate VirtualListContainer')
let node = this.refs.node let node = this.refs.node
let storeKey = this.get('storeKey') let realm = this.get('realm')
let cachedStore this.store.set({currentRealm: realm})
if (storeKey && (cachedStore = cachedVirtualStores[storeKey])) { let scrollTop = this.store.get('scrollTop')
this.store.set({ if (scrollTop > 0) {
scrollTop: cachedStore.state.scrollTop,
scrollHeight: cachedStore.state.scrollHeight,
offsetHeight: cachedStore.state.offsetHeight
})
this.store.set(cachedStore.state)
// rehydrate scroll top
let cachedScrollTop = cachedStore.state.scrollTop || 0
if (cachedScrollTop === 0) {
if (process.env.NODE_ENV !== 'production') {
console.log('no need to force scroll top')
}
return
}
let initializedScrollTop = false
let node = this.refs.node
this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => { this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.log('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight) console.log('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight)
} }
if (!initializedScrollTop && allVisibleItemsHaveHeight && node) { if (!this.get('initializedScrollTop') && allVisibleItemsHaveHeight && node) {
initializedScrollTop = true this.set({'initializedScrollTop': true})
requestAnimationFrame(() => { requestAnimationFrame(() => {
mark('set scrollTop') mark('set scrollTop')
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.log('forcing scroll top to ', cachedScrollTop) console.log('forcing scroll top to ', scrollTop)
} }
node.scrollTop = cachedScrollTop node.scrollTop = scrollTop
stop('set scrollTop') stop('set scrollTop')
}) })
} }
}) })
} else { } else {
this.store.set({ this.store.setForRealm({
scrollTop: 0,
scrollHeight: node.scrollHeight, scrollHeight: node.scrollHeight,
offsetHeight: node.offsetHeight offsetHeight: node.offsetHeight
}) })
} }
stop('onCreate VirtualListContainer') stop('onCreate VirtualListContainer')
}, },
ondestroy() {
let storeKey = this.get('storeKey')
if (storeKey) {
let clonedState = this.store.cloneState()
if (process.env.NODE_ENV !== 'production') {
console.log('caching scroll top', clonedState.scrollTop)
}
cachedVirtualStores[storeKey] = {
state: clonedState
}
}
},
store: () => virtualListStore, store: () => virtualListStore,
events: { events: {
scroll(node, callback) { scroll(node, callback) {
@ -116,7 +81,7 @@
}, },
methods: { methods: {
onScroll(event) { onScroll(event) {
this.store.set({ this.store.setForRealm({
scrollTop: event.target.scrollTop, scrollTop: event.target.scrollTop,
scrollHeight: event.target.scrollHeight scrollHeight: event.target.scrollHeight
}) })

View file

@ -3,28 +3,12 @@ import { mark, stop } from '../../_utils/marks'
const VIEWPORT_RENDER_FACTOR = 4 const VIEWPORT_RENDER_FACTOR = 4
const cloneKeys = [
'items',
'itemHeights',
'scrollTop',
'scrollHeight',
'offsetHeight'
]
class VirtualListStore extends Store { class VirtualListStore extends Store {
constructor(state) { constructor(state) {
super(state) super(state)
this._batches = {} this._batches = {}
} }
cloneState() {
let res = {}
for (let key of cloneKeys) {
res[key] = this.get(key)
}
return res
}
batchUpdate(key, subKey, value) { batchUpdate(key, subKey, value) {
let batch = this._batches[key] let batch = this._batches[key]
if (!batch) { if (!batch) {
@ -53,15 +37,42 @@ class VirtualListStore extends Store {
stop('batchUpdate()') stop('batchUpdate()')
}) })
} }
setForRealm(obj) {
let realmName = this.get('currentRealm')
let realms = this.get('realms') || {}
realms[realmName] = Object.assign(realms[realmName] || {}, obj)
this.set({realms: realms})
}
} }
const virtualListStore = new VirtualListStore({ const virtualListStore = new VirtualListStore({
items: [], realms: {},
currentRealm: null,
itemHeights: {}, itemHeights: {},
showFooter: false,
footerHeight: 0 footerHeight: 0
}) })
virtualListStore.compute('items', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].items || []
})
virtualListStore.compute('showFooter', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].showFooter
})
virtualListStore.compute('scrollTop', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].scrollTop || 0
})
virtualListStore.compute('scrollHeight', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].scrollHeight || 0
})
virtualListStore.compute('offsetHeight', ['currentRealm', 'realms'], (currentRealm, realms) => {
return realms[currentRealm] && realms[currentRealm].offsetHeight || 0
})
virtualListStore.compute('visibleItems', virtualListStore.compute('visibleItems',
['items', 'scrollTop', 'itemHeights', 'offsetHeight'], ['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
(items, scrollTop, itemHeights, offsetHeight) => { (items, scrollTop, itemHeights, offsetHeight) => {

View file

@ -4,7 +4,7 @@
<Layout page='tags' <Layout page='tags'
virtual="true" virtual="true"
virtualStoreKey='account/{{params.accountId}}' virtualRealm='account/{{params.accountId}}'
dynamicPage="{{profileName}}" dynamicPage="{{profileName}}"
dynamicHref="/accounts/{{params.accountId}}" dynamicHref="/accounts/{{params.accountId}}"
dynamicLabel="{{shortProfileName}}" dynamicLabel="{{shortProfileName}}"

View file

@ -2,7 +2,7 @@
<title>Pinafore Federated</title> <title>Pinafore Federated</title>
</:Head> </:Head>
<Layout page='federated' virtual="true" virtualStoreKey="federated"> <Layout page='federated' virtual="true" virtualRealm="federated">
{{#if $isUserLoggedIn}} {{#if $isUserLoggedIn}}
<LazyTimeline timeline='federated' /> <LazyTimeline timeline='federated' />
{{else}} {{else}}

View file

@ -2,7 +2,7 @@
<title>Pinafore Home</title> <title>Pinafore Home</title>
</:Head> </:Head>
<Layout page='home' virtual="true" virtualStoreKey="home"> <Layout page='home' virtual="true" virtualRealm="home">
{{#if $isUserLoggedIn}} {{#if $isUserLoggedIn}}
<LazyTimeline timeline='home' /> <LazyTimeline timeline='home' />
{{else}} {{else}}

View file

@ -2,7 +2,7 @@
<title>Pinafore Local</title> <title>Pinafore Local</title>
</:Head> </:Head>
<Layout page='local' virtual="true" virtualStoreKey="local"> <Layout page='local' virtual="true" virtualRealm="local">
{{#if $isUserLoggedIn}} {{#if $isUserLoggedIn}}
<LazyTimeline timeline='local' /> <LazyTimeline timeline='local' />
{{else}} {{else}}

View file

@ -2,7 +2,7 @@
<title>Pinafore Notifications</title> <title>Pinafore Notifications</title>
</:Head> </:Head>
<Layout page='notifications' virtual="true" virtualStoreKey="federated"> <Layout page='notifications' virtual="true" virtualRealm="federated">
<HiddenFromSSR> <HiddenFromSSR>
<FreeTextLayout> <FreeTextLayout>
<h1>Notifications</h1> <h1>Notifications</h1>

View file

@ -4,7 +4,7 @@
<Layout page='tags' <Layout page='tags'
virtual="true" virtual="true"
virtualStoreKey='tag/{{params.tagName}}' virtualRealm='tag/{{params.tagName}}'
dynamicPage="{{params.tagName}}" dynamicPage="{{params.tagName}}"
dynamicHref="/tags/{{params.tagName}}" dynamicHref="/tags/{{params.tagName}}"
dynamicLabel="{{'#' + params.tagName}}" dynamicLabel="{{'#' + params.tagName}}"