save scroll positions

This commit is contained in:
Nolan Lawson 2018-01-24 09:47:31 -08:00
parent 208316b914
commit fb234adb79
10 changed files with 108 additions and 18 deletions

View File

@ -1,10 +1,18 @@
<Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/>
<VirtualListContainer>
<main>
<slot></slot>
</main>
</VirtualListContainer>
{{#if virtual}}
<VirtualListContainer storeKey="{{virtualStoreKey}}">
<main>
<slot></slot>
</main>
</VirtualListContainer>
{{else}}
<div class="container">
<main>
<slot></slot>
</main>
</div>
{{/if}}
<script>
import Nav from './Nav.html';
import VirtualListContainer from './VirtualListContainer.html'

View File

@ -7,6 +7,8 @@
shown="{{initialized}}"
footerComponent="{{LoadingFooter}}"
showFooter="{{initialized && runningUpdate}}"
storeKey="{{timeline}}"
initialized="{{initialized}}"
/>
</div>
<style>
@ -27,19 +29,33 @@
import { toast } from '../_utils/toast'
import { database } from '../_utils/database/database'
const cachedTimelines = {}
if (process.browser && process.env.NODE_ENV !== 'production') {
window.cachedTimelines = cachedTimelines
}
const FETCH_LIMIT = 20
export default {
async oncreate() {
let statuses = await this.fetchStatusesAndPossiblyFallBack()
this.addStatuses(statuses)
let timeline = this.get('timeline')
let cachedStatusIds = cachedTimelines[timeline]
if (cachedStatusIds) {
this.set({statusIds: cachedStatusIds})
} else {
this.addStatuses(await this.fetchStatusesAndPossiblyFallBack())
}
requestAnimationFrame(() => {
requestAnimationFrame((() => {
requestAnimationFrame(() => {
this.set({initialized: true})
this.fire('initialized')
}))
})
})
},
ondestroy() {
cachedTimelines[this.get('timeline')] = this.get('statusIds')
},
data: () => ({
StatusListItem: StatusListItem,
LoadingFooter: LoadingFooter,

View File

@ -13,17 +13,44 @@
const SCROLL_EVENT_DELAY = 300
const cachedVirtualStores = {}
if (process.browser && process.env.NODE_ENV !== 'production') {
window.cachedVirtualStores = cachedVirtualStores
}
export default {
oncreate() {
mark('onCreate VirtualListContainer')
let node = this.refs.node
this.store.set({
scrollTop: node.scrollTop,
scrollHeight: node.scrollHeight,
offsetHeight: node.offsetHeight
})
let storeKey = this.get('storeKey')
let cachedStore
if (storeKey && (cachedStore = cachedVirtualStores[storeKey])) {
this.store.set({
scrollTop: cachedStore.state.scrollTop,
scrollHeight: cachedStore.state.scrollHeight,
offsetHeight: cachedStore.state.offsetHeight
})
this.rehydrateScrollTop(cachedStore)
this.store.set(cachedStore.state)
} else {
this.store.set({
scrollTop: 0,
scrollHeight: node.scrollHeight,
offsetHeight: node.offsetHeight
})
}
stop('onCreate VirtualListContainer')
},
ondestroy() {
let storeKey = this.get('storeKey')
if (storeKey) {
cachedVirtualStores[storeKey] = {
state: this.store.cloneState(),
height: this.store.get('height')
}
}
},
store: () => virtualListStore,
events: {
scroll(node, callback) {
@ -70,6 +97,25 @@
console.log('is fullscreen? ', isFullscreen())
this.set({ fullscreen: isFullscreen() })
stop('onFullscreenChange')
},
rehydrateScrollTop(cachedStore) {
let cachedScrollTop = cachedStore.state.scrollTop || 0
let cachedHeight = cachedStore.height
if (cachedScrollTop === 0) {
return
}
let initializedScrollTop = false
let node = this.refs.node
this.store.observe('height', height => {
if (!initializedScrollTop && height === cachedHeight && node) {
initializedScrollTop = true
requestAnimationFrame(() => {
mark('set scrollTop')
node.scrollTop = cachedScrollTop
stop('set scrollTop')
})
}
})
}
}
};

View File

@ -3,12 +3,28 @@ import { mark, stop } from '../_utils/marks'
const VIEWPORT_RENDER_FACTOR = 4
const cloneKeys = [
'items',
'itemHeights',
'scrollTop',
'scrollHeight',
'offsetHeight'
]
class VirtualListStore extends Store {
constructor(state) {
super(state)
this._batches = {}
}
cloneState() {
let res = {}
for (let key of cloneKeys) {
res[key] = this.get(key)
}
return res
}
batchUpdate(key, subKey, value) {
let batch = this._batches[key]
if (!batch) {

View File

@ -3,6 +3,8 @@
</:Head>
<Layout page='tags'
virtual="true"
virtualStoreKey='account/{{params.accountId}}'
dynamicPage="{{profileName}}"
dynamicHref="/accounts/{{params.accountId}}"
dynamicLabel="{{shortProfileName}}"

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,8 @@
</:Head>
<Layout page='tags'
virtual="true"
virtualStoreKey='tag/{{params.tagName}}'
dynamicPage="{{params.tagName}}"
dynamicHref="/tags/{{params.tagName}}"
dynamicLabel="{{'#' + params.tagName}}"