more work on pseudo-virtual list

This commit is contained in:
Nolan Lawson 2018-01-30 18:26:13 -08:00
parent 98b22e0243
commit d2fe8b29f4
6 changed files with 71 additions and 28 deletions

View file

@ -7,9 +7,8 @@
makeProps="{{makeProps}}" makeProps="{{makeProps}}"
key="{{wrappedItem.item}}" key="{{wrappedItem.item}}"
intersectionObserver="{{intersectionObserver}}" intersectionObserver="{{intersectionObserver}}"
hide="{{shouldHide(wrappedItem.item, intersectionStates)}}" hide="{{shouldHide(wrappedItem.item, $intersectionStates)}}"
height="{{getHeight(wrappedItem.item, intersectionStates)}}" height="{{getHeight(wrappedItem.item, $intersectionStates)}}"
on:renderedListItem="onRenderedListItem()"
/> />
{{/each}} {{/each}}
</div> </div>
@ -24,16 +23,26 @@
import PseudoVirtualListLazyItem from './PseudoVirtualListLazyItem.html' import PseudoVirtualListLazyItem from './PseudoVirtualListLazyItem.html'
import { getRectFromEntry } from '../../_utils/getRectFromEntry' import { getRectFromEntry } from '../../_utils/getRectFromEntry'
import { mark, stop } from '../../_utils/marks' import { mark, stop } from '../../_utils/marks'
import { pseudoVirtualListStore } from './pseudoVirtualListStore'
export default { export default {
oncreate() { oncreate() {
this.set({ mark('PseudoVirtualList oncreate()')
intersectionObserver: new IntersectionObserver(this.onIntersection.bind(this), { this.store.set({currentRealm: this.get('realm')})
root: document.getElementsByClassName('container')[0], // TODO: fix this
rootMargin: '300% 0px' // When re-rendering, assume all items are non-intersecting until told otherwise.
}), // We already have the heights cached.
intersectionStates: {} let intersectionStates = this.store.get('intersectionStates')
}) let keys = Object.keys(intersectionStates)
for (let key of keys) {
intersectionStates[key].isIntersecting = false
}
this.store.setForRealm({intersectionStates: intersectionStates})
this.set({intersectionObserver: new IntersectionObserver(this.onIntersection.bind(this), {
root: document.getElementsByClassName('container')[0], // TODO: fix this
rootMargin: '300% 0px'
})})
this.observe('allItemsHaveHeight', allItemsHaveHeight => { this.observe('allItemsHaveHeight', allItemsHaveHeight => {
console.log('allItemsHaveHeight', allItemsHaveHeight) console.log('allItemsHaveHeight', allItemsHaveHeight)
if (allItemsHaveHeight && !this.get('initialized')) { if (allItemsHaveHeight && !this.get('initialized')) {
@ -42,6 +51,7 @@
this.fire('initializedVisibleItems') this.fire('initializedVisibleItems')
} }
}) })
stop('PseudoVirtualList oncreate()')
}, },
ondestroy() { ondestroy() {
let intersectionObserver = this.get('intersectionObserver') let intersectionObserver = this.get('intersectionObserver')
@ -49,15 +59,12 @@
intersectionObserver.disconnect() intersectionObserver.disconnect()
} }
}, },
data: () => ({
intersectionStates: {}
}),
helpers: { helpers: {
shouldHide(key, intersectionStates) { shouldHide(key, $intersectionStates) {
return !!(intersectionStates[key] && !intersectionStates[key].isIntersecting) return !!($intersectionStates[key] && !$intersectionStates[key].isIntersecting)
}, },
getHeight(key, intersectionStates) { getHeight(key, $intersectionStates) {
return intersectionStates[key] && intersectionStates[key].rect.height return $intersectionStates[key] && $intersectionStates[key].rect.height
} }
}, },
methods: { methods: {
@ -85,7 +92,7 @@
mark('onIntersection') mark('onIntersection')
let newIntersectionStates = {} let newIntersectionStates = {}
let scrollToItem = this.get('scrollToItem') let scrollToItem = this.get('scrollToItem')
let intersectionStates = this.get('intersectionStates') let intersectionStates = this.store.get('intersectionStates')
for (let entry of entries) { for (let entry of entries) {
let key = entry.target.getAttribute('pseudo-virtual-list-key') let key = entry.target.getAttribute('pseudo-virtual-list-key')
let rect = getRectFromEntry(entry) let rect = getRectFromEntry(entry)
@ -98,18 +105,18 @@
} }
} }
intersectionStates = Object.assign(intersectionStates, newIntersectionStates) intersectionStates = Object.assign(intersectionStates, newIntersectionStates)
this.set({intersectionStates: intersectionStates}) this.store.setForRealm({intersectionStates: intersectionStates})
stop('onIntersection') stop('onIntersection')
} }
}, },
computed: { computed: {
wrappedItems: (items) => items.map(item => ({item: item})), wrappedItems: (items) => items.map(item => ({item: item})),
allItemsHaveHeight: (items, intersectionStates) => { allItemsHaveHeight: (items, $intersectionStates) => {
if (!items.length) { if (!items.length) {
return false return false
} }
for (let item of items) { for (let item of items) {
if (!intersectionStates[item]) { if (!$intersectionStates[item]) {
return false return false
} }
} }
@ -118,6 +125,7 @@
}, },
components: { components: {
PseudoVirtualListLazyItem PseudoVirtualListLazyItem
} },
store: () => pseudoVirtualListStore
} }
</script> </script>

View file

@ -0,0 +1,24 @@
import { Store } from 'svelte/store.js'
class PseudoVirtualListStore extends Store {
setForRealm(obj) {
let realmName = this.get('currentRealm')
let realms = this.get('realms') || {}
realms[realmName] = Object.assign(realms[realmName] || {}, obj)
this.set({realms: realms})
}
}
const pseudoVirtualListStore = new PseudoVirtualListStore()
pseudoVirtualListStore.compute('intersectionStates',
['realms', 'currentRealm'],
(realms, currentRealm) => {
return (realms && realms[currentRealm] && realms[currentRealm].intersectionStates) || {}
})
if (process.browser && process.env.NODE_NODE !== 'production') {
window.pseudoVirtualListStore = pseudoVirtualListStore
}
export { pseudoVirtualListStore }

View file

@ -19,7 +19,7 @@
<span class="status-author-handle"> <span class="status-author-handle">
{{'@' + originalAccount.acct}} {{'@' + originalAccount.acct}}
</span> </span>
{{#if timelineType === 'status'}} {{#if isStatusInContext}}
<ExternalLink class="status-author-date" href="{{originalStatus.url}}" showIcon="true"> <ExternalLink class="status-author-date" href="{{originalStatus.url}}" showIcon="true">
<time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time> <time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time>
</ExternalLink> </ExternalLink>
@ -39,7 +39,7 @@
</div> </div>
{{/if}} {{/if}}
{{#if !originalStatus.spoiler_text || spoilerShown}} {{#if !originalStatus.spoiler_text || spoilerShown}}
<div class="status-content" ref:contentNode> <div class="status-content {{isStatusInContext ? 'status-in-context' : ''}}" ref:contentNode>
{{{emojifiedContent}}} {{{emojifiedContent}}}
</div> </div>
{{/if}} {{/if}}
@ -176,6 +176,11 @@
font-size: 0.9em; font-size: 0.9em;
} }
.status-content.status-in-context {
font-size: 1.1em;
margin: 20px 10px 20px 5px;
}
:global(.status-content .status-emoji) { :global(.status-content .status-emoji) {
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -323,6 +328,9 @@
}, },
store: () => store, store: () => store,
computed: { computed: {
isStatusInContext: (timelineType, timelineValue, statusId) => {
return timelineType === 'status' && timelineValue === statusId
},
createdAtDate: (status) => status.created_at, createdAtDate: (status) => status.created_at,
relativeDate: (createdAtDate) => { relativeDate: (createdAtDate) => {
mark('compute relativeDate') mark('compute relativeDate')

View file

@ -19,15 +19,15 @@
min-height: 60vh; min-height: 60vh;
} }
.loading-page { .loading-page {
position: absolute; position: fixed;
top: 0; top: 72px;
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 50 z-index: 50;
} }
</style> </style>
<script> <script>

View file

@ -1,5 +1,6 @@
<Status status="{{virtualProps.status}}" <Status status="{{virtualProps.status}}"
timelineType="{{virtualProps.timelineType}}" timelineType="{{virtualProps.timelineType}}"
timelineValue="{{virtualProps.timelineValue}}"
index="{{virtualIndex}}" index="{{virtualIndex}}"
length="{{virtualLength}}" length="{{virtualLength}}"
on:recalculateHeight /> on:recalculateHeight />

View file

@ -19,6 +19,7 @@
shown="{{$initialized}}" shown="{{$initialized}}"
on:initializedVisibleItems="initialize()" on:initializedVisibleItems="initialize()"
scrollToItem="{{timelineValue}}" scrollToItem="{{timelineValue}}"
realm="{{$currentInstance + '/' + timeline}}"
/> />
{{/if}} {{/if}}
</div> </div>
@ -49,8 +50,9 @@
Status Status
}), }),
computed: { computed: {
makeProps: ($currentInstance, timelineType) => async (statusId) => ({ makeProps: ($currentInstance, timelineType, timelineValue) => async (statusId) => ({
timelineType: timelineType, timelineType: timelineType,
timelineValue: timelineValue,
status: await database.getStatus($currentInstance, statusId) status: await database.getStatus($currentInstance, statusId)
}), }),
label: (timeline, $currentInstance, timelineType, timelineValue) => { label: (timeline, $currentInstance, timelineType, timelineValue) => {