more work on pseudo-virtual list
This commit is contained in:
parent
98b22e0243
commit
d2fe8b29f4
|
@ -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>
|
|
@ -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 }
|
|
@ -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')
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
Loading…
Reference in a new issue