forked from cybrespace/pinafore
		
	save scroll positions
This commit is contained in:
		
							parent
							
								
									208316b914
								
							
						
					
					
						commit
						fb234adb79
					
				
					 10 changed files with 108 additions and 18 deletions
				
			
		|  | @ -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' | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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') | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| </:Head> | ||||
| 
 | ||||
| <Layout page='tags' | ||||
|         virtual="true" | ||||
|         virtualStoreKey='account/{{params.accountId}}' | ||||
|         dynamicPage="{{profileName}}" | ||||
|         dynamicHref="/accounts/{{params.accountId}}" | ||||
|         dynamicLabel="{{shortProfileName}}" | ||||
|  |  | |||
|  | @ -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}} | ||||
|  |  | |||
|  | @ -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}} | ||||
|  |  | |||
|  | @ -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}} | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 	<title>Pinafore – Notifications</title> | ||||
| </:Head> | ||||
| 
 | ||||
| <Layout page='notifications'> | ||||
| <Layout page='notifications' virtual="true" virtualStoreKey="federated"> | ||||
|   <HiddenFromSSR> | ||||
|     <FreeTextLayout> | ||||
|       <h1>Notifications</h1> | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| </:Head> | ||||
| 
 | ||||
| <Layout page='tags' | ||||
|         virtual="true" | ||||
|         virtualStoreKey='tag/{{params.tagName}}' | ||||
|         dynamicPage="{{params.tagName}}" | ||||
|         dynamicHref="/tags/{{params.tagName}}" | ||||
|         dynamicLabel="{{'#' + params.tagName}}" | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue