forked from cybrespace/pinafore
streaming is kinda working
This commit is contained in:
parent
075066ba9a
commit
2a86425c90
|
@ -16,17 +16,13 @@ async function removeDuplicates (instanceName, timelineName, updates) {
|
|||
}
|
||||
|
||||
async function handleFreshChanges (instanceName, timelineName) {
|
||||
console.log('handleFreshChanges')
|
||||
let freshChanges = store.getForTimeline(instanceName, timelineName, 'freshChanges')
|
||||
console.log('freshChanges', freshChanges)
|
||||
if (freshChanges.updates && freshChanges.updates.length) {
|
||||
let updates = freshChanges.updates.slice()
|
||||
freshChanges.updates = []
|
||||
store.setForTimeline(instanceName, timelineName, {freshChanges: freshChanges})
|
||||
|
||||
console.log('before removing duplicates, updates are ', updates.length)
|
||||
updates = await removeDuplicates(instanceName, timelineName, updates)
|
||||
console.log('after removing duplicates, updates are ', updates.length)
|
||||
|
||||
await database.insertTimelineItems(instanceName, timelineName, updates)
|
||||
|
||||
|
@ -39,7 +35,6 @@ async function handleFreshChanges (instanceName, timelineName) {
|
|||
}
|
||||
|
||||
function handleStreamMessage (instanceName, timelineName, message) {
|
||||
console.log('handleStreamMessage')
|
||||
let { event, payload } = message
|
||||
let key = event === 'update' ? 'updates' : 'deletes'
|
||||
let freshChanges = store.getForTimeline(instanceName, timelineName, 'freshChanges') || {}
|
||||
|
@ -54,7 +49,6 @@ function handleStreamMessage (instanceName, timelineName, message) {
|
|||
export function createStream (streamingApi, instanceName, accessToken, timelineName) {
|
||||
return new TimelineStream(streamingApi, accessToken, timelineName, {
|
||||
onMessage (msg) {
|
||||
console.log('message', msg)
|
||||
if (msg.event !== 'update' && msg.event !== 'delete') {
|
||||
console.error("don't know how to handle event", msg)
|
||||
return
|
||||
|
@ -73,4 +67,4 @@ export function createStream (streamingApi, instanceName, accessToken, timelineN
|
|||
console.log('reconnected stream for timeline', timelineName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -81,3 +81,15 @@ export async function fetchTimelineItemsOnScrollToBottom () {
|
|||
await fetchTimelineItemsAndPossiblyFallBack()
|
||||
store.setForTimeline(instanceName, timelineName, { runningUpdate: false })
|
||||
}
|
||||
|
||||
export async function showMoreItemsForCurrentTimeline() {
|
||||
let instanceName = store.get('currentInstance')
|
||||
let timelineName = store.get('currentTimeline')
|
||||
let itemIdsToAdd = store.get('itemIdsToAdd')
|
||||
addTimelineItemIds(instanceName, timelineName, itemIdsToAdd)
|
||||
store.setForTimeline(instanceName, timelineName, {
|
||||
itemIdsToAdd: [],
|
||||
shouldShowHeader: false,
|
||||
showHeader: false
|
||||
})
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<div class="more-items-header" role="dialog">
|
||||
<button class="primary" type="button" on:click="onClick(event)">
|
||||
Click to show {{count}} more
|
||||
</button>
|
||||
</div>
|
||||
<style>
|
||||
.more-items-header {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
align-items: center;
|
||||
justify-content:center;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
onClick(event) {
|
||||
let onClick = this.get('onClick')
|
||||
if (onClick) {
|
||||
onClick(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,11 @@
|
|||
<MoreHeader count="{{virtualProps.count}}"
|
||||
onClick="{{virtualProps.onClick}}"
|
||||
/>
|
||||
<script>
|
||||
import MoreHeader from './MoreHeader.html'
|
||||
export default {
|
||||
components: {
|
||||
MoreHeader
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -12,9 +12,13 @@
|
|||
:makeProps
|
||||
items="{{$timelineItemIds}}"
|
||||
on:scrollToBottom="onScrollToBottom()"
|
||||
on:scrollToTop="onScrollToTop()"
|
||||
shown="{{$initialized}}"
|
||||
footerComponent="{{LoadingFooter}}"
|
||||
showFooter="{{$initialized && $runningUpdate}}"
|
||||
showHeader="{{$showHeader}}"
|
||||
headerComponent="{{MoreHeaderVirtualWrapper}}"
|
||||
:headerProps
|
||||
realm="{{$currentInstance + '/' + timeline}}"
|
||||
on:initializedVisibleItems="initialize()"
|
||||
/>
|
||||
|
@ -23,8 +27,12 @@
|
|||
:makeProps
|
||||
items="{{$timelineItemIds}}"
|
||||
on:scrollToBottom="onScrollToBottom()"
|
||||
on:scrollToTop="onScrollToTop()"
|
||||
shown="{{$initialized}}"
|
||||
footerComponent="{{LoadingFooter}}"
|
||||
showHeader="{{$showHeader}}"
|
||||
headerComponent="{{MoreHeaderVirtualWrapper}}"
|
||||
:headerProps
|
||||
showFooter="{{$initialized && $runningUpdate}}"
|
||||
realm="{{$currentInstance + '/' + timeline}}"
|
||||
on:initializedVisibleItems="initialize()"
|
||||
|
@ -55,13 +63,16 @@
|
|||
import Status from '../status/Status.html'
|
||||
import PseudoVirtualList from '../pseudoVirtualList/PseudoVirtualList.html'
|
||||
import LoadingFooter from './LoadingFooter.html'
|
||||
import MoreHeaderVirtualWrapper from './MoreHeaderVirtualWrapper.html'
|
||||
import VirtualList from '../virtualList/VirtualList.html'
|
||||
import { timelines } from '../../_static/timelines'
|
||||
import { database } from '../../_database/database'
|
||||
import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
|
||||
import LoadingPage from '../LoadingPage.html'
|
||||
import { focusWithCapture, blurWithCapture } from '../../_utils/events'
|
||||
import { addTimelineItemIds } from '../../_actions/timeline'
|
||||
import { showMoreItemsForCurrentTimeline } from '../../_actions/timeline'
|
||||
import { virtualListStore } from '../virtualList/virtualListStore' // TODO: hacky, need better way to expose scrollTop
|
||||
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
|
@ -71,15 +82,7 @@
|
|||
if (this.store.get('initialized')) {
|
||||
this.restoreFocus()
|
||||
}
|
||||
let instanceName = this.store.get('currentInstance')
|
||||
let timelineName = this.get('timeline')
|
||||
this.observe('itemIdsToAdd', itemIdsToAdd => {
|
||||
console.log('itemIdsToAdd', itemIdsToAdd)
|
||||
if (itemIdsToAdd && itemIdsToAdd.length) {
|
||||
addTimelineItemIds(instanceName, timelineName, itemIdsToAdd)
|
||||
this.store.setForTimeline(instanceName, timelineName, {itemIdsToAdd: []})
|
||||
}
|
||||
})
|
||||
this.setupStreaming()
|
||||
},
|
||||
ondestroy() {
|
||||
console.log('ondestroy')
|
||||
|
@ -89,6 +92,7 @@
|
|||
StatusVirtualListItem,
|
||||
NotificationVirtualListItem,
|
||||
LoadingFooter,
|
||||
MoreHeaderVirtualWrapper,
|
||||
Status
|
||||
}),
|
||||
computed: {
|
||||
|
@ -139,6 +143,12 @@
|
|||
$timelines[$currentInstance] &&
|
||||
$timelines[$currentInstance][timeline] &&
|
||||
$timelines[$currentInstance][timeline].itemIdsToAdd) || []
|
||||
},
|
||||
headerProps: (itemIdsToAdd) => {
|
||||
return {
|
||||
count: (itemIdsToAdd && itemIdsToAdd.length) || 0,
|
||||
onClick: showMoreItemsForCurrentTimeline
|
||||
}
|
||||
}
|
||||
},
|
||||
store: () => store,
|
||||
|
@ -168,6 +178,41 @@
|
|||
}
|
||||
fetchTimelineItemsOnScrollToBottom()
|
||||
},
|
||||
onScrollToTop() {
|
||||
if (this.store.get('shouldShowHeader')) {
|
||||
this.store.setForCurrentTimeline({
|
||||
showHeader: true,
|
||||
shouldShowHeader: false
|
||||
})
|
||||
}
|
||||
},
|
||||
setupStreaming() {
|
||||
let instanceName = this.store.get('currentInstance')
|
||||
let timelineName = this.get('timeline')
|
||||
let handleItemIdsToAdd = () => {
|
||||
let itemIdsToAdd = this.get('itemIdsToAdd')
|
||||
if (!itemIdsToAdd || !itemIdsToAdd.length) {
|
||||
return
|
||||
}
|
||||
let scrollTop = virtualListStore.get('scrollTop')
|
||||
let shouldShowHeader = this.store.get('shouldShowHeader')
|
||||
let showHeader = this.store.get('showHeader')
|
||||
//console.log('handleItemIdsToAdd', (itemIdsToAdd && itemIdsToAdd.length) || 0)
|
||||
if (scrollTop === 0 && !shouldShowHeader && !showHeader) {
|
||||
// if the user is scrolled to the top and we're not showing the header, then
|
||||
// just insert the statuses. this is "chat room mode"
|
||||
showMoreItemsForCurrentTimeline()
|
||||
} else {
|
||||
// user hasn't scrolled to the top, show a header instead
|
||||
this.store.setForTimeline(instanceName, timelineName, {shouldShowHeader: true})
|
||||
}
|
||||
}
|
||||
this.observe('itemIdsToAdd', itemIdsToAdd => {
|
||||
if (itemIdsToAdd && itemIdsToAdd.length) {
|
||||
scheduleIdleTask(handleItemIdsToAdd)
|
||||
}
|
||||
})
|
||||
},
|
||||
setupFocus() {
|
||||
this.onPushState = this.onPushState.bind(this)
|
||||
this.store.setForCurrentTimeline({ignoreBlurEvents: false})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!-- TODO: setting height is hacky, just make this element the scroller -->
|
||||
<div class="virtual-list {{shown ? '' : 'hidden'}}" style="height: {{$height}}px;">
|
||||
<VirtualListHeader component="{{headerComponent}}" virtualProps="{{headerProps}}" shown="{{$showHeader}}"/>
|
||||
{{#if $visibleItems}}
|
||||
{{#each $visibleItems as visibleItem @key}}
|
||||
<VirtualListLazyItem :component
|
||||
|
@ -23,25 +23,32 @@
|
|||
<script>
|
||||
import VirtualListLazyItem from './VirtualListLazyItem'
|
||||
import VirtualListFooter from './VirtualListFooter.html'
|
||||
import VirtualListHeader from './VirtualListHeader.html'
|
||||
import { virtualListStore } from './virtualListStore'
|
||||
import throttle from 'lodash/throttle'
|
||||
import { mark, stop } from '../../_utils/marks'
|
||||
|
||||
const DISTANCE_FROM_BOTTOM_TO_FIRE = 400
|
||||
const SCROLL_TO_BOTTOM_DELAY = 1000
|
||||
const SCROLL_EVENT_THROTTLE = 1000
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
this.observe('showFooter', showFooter => {
|
||||
this.store.setForRealm({showFooter: showFooter})
|
||||
})
|
||||
this.observe('showHeader', showHeader => {
|
||||
this.store.setForRealm({showHeader: showHeader})
|
||||
})
|
||||
this.observe('items', (items) => {
|
||||
mark('set items')
|
||||
this.store.setForRealm({items: items})
|
||||
stop('set items')
|
||||
this.fireScrollToBottom = throttle(() => {
|
||||
this.fire('scrollToBottom')
|
||||
}, SCROLL_TO_BOTTOM_DELAY)
|
||||
}, SCROLL_EVENT_THROTTLE)
|
||||
this.fireScrollToTop = throttle(() => {
|
||||
this.fire('scrollToTop')
|
||||
}, SCROLL_EVENT_THROTTLE)
|
||||
})
|
||||
|
||||
this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => {
|
||||
|
@ -62,6 +69,12 @@
|
|||
this.fireScrollToBottom()
|
||||
}
|
||||
})
|
||||
|
||||
this.observe('distanceFromTop', (distanceFromTop) => {
|
||||
if (distanceFromTop === 0) {
|
||||
this.fireScrollToTop()
|
||||
}
|
||||
})
|
||||
},
|
||||
data: () => ({
|
||||
component: null
|
||||
|
@ -69,12 +82,14 @@
|
|||
store: () => virtualListStore,
|
||||
components: {
|
||||
VirtualListLazyItem,
|
||||
VirtualListFooter
|
||||
VirtualListFooter,
|
||||
VirtualListHeader
|
||||
},
|
||||
computed: {
|
||||
distanceFromBottom: ($scrollHeight, $scrollTop, $offsetHeight) => {
|
||||
return $scrollHeight - $scrollTop - $offsetHeight
|
||||
},
|
||||
distanceFromTop: ($scrollTop) => $scrollTop,
|
||||
// TODO: bug in svelte store, shouldn't need to do this
|
||||
allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<div class="virtual-list-header {{shown ? 'shown' : ''}}"
|
||||
aria-hidden="{{!shown}}"
|
||||
ref:node >
|
||||
<:Component {component} :virtualProps />
|
||||
</div>
|
||||
<style>
|
||||
.virtual-list-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.virtual-list-header.shown {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { virtualListStore } from './virtualListStore'
|
||||
import { AsyncLayout } from '../../_utils/AsyncLayout'
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
const asyncLayout = new AsyncLayout(() => '__header__')
|
||||
asyncLayout.observe('__header__', this.refs.node, (rect) => {
|
||||
asyncLayout.disconnect()
|
||||
this.store.setForRealm({headerHeight: rect.height})
|
||||
})
|
||||
},
|
||||
store: () => virtualListStore,
|
||||
}
|
||||
</script>
|
|
@ -14,21 +14,23 @@ const virtualListStore = new VirtualListStore()
|
|||
virtualListStore.computeForRealm('items', null)
|
||||
virtualListStore.computeForRealm('showFooter', false)
|
||||
virtualListStore.computeForRealm('footerHeight', 0)
|
||||
virtualListStore.computeForRealm('showHeader', false)
|
||||
virtualListStore.computeForRealm('headerHeight', 0)
|
||||
virtualListStore.computeForRealm('scrollTop', 0)
|
||||
virtualListStore.computeForRealm('scrollHeight', 0)
|
||||
virtualListStore.computeForRealm('offsetHeight', 0)
|
||||
virtualListStore.computeForRealm('itemHeights', {})
|
||||
|
||||
virtualListStore.compute('visibleItems',
|
||||
['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
|
||||
(items, scrollTop, itemHeights, offsetHeight) => {
|
||||
['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'showHeader', 'headerHeight'],
|
||||
(items, scrollTop, itemHeights, offsetHeight, showHeader, headerHeight) => {
|
||||
mark('compute visibleItems')
|
||||
if (!items) {
|
||||
return null
|
||||
}
|
||||
let renderBuffer = VIEWPORT_RENDER_FACTOR * offsetHeight
|
||||
let visibleItems = []
|
||||
let totalOffset = 0
|
||||
let totalOffset = showHeader ? headerHeight : 0
|
||||
let len = items.length
|
||||
let i = -1
|
||||
while (++i < len) {
|
||||
|
@ -57,12 +59,12 @@ virtualListStore.compute('visibleItems',
|
|||
})
|
||||
|
||||
virtualListStore.compute('heightWithoutFooter',
|
||||
['items', 'itemHeights'],
|
||||
(items, itemHeights) => {
|
||||
['items', 'itemHeights', 'showHeader', 'headerHeight'],
|
||||
(items, itemHeights, showHeader, headerHeight) => {
|
||||
if (!items) {
|
||||
return 0
|
||||
}
|
||||
let sum = 0
|
||||
let sum = showHeader ? headerHeight : 0
|
||||
let i = -1
|
||||
let len = items.length
|
||||
while (++i < len) {
|
||||
|
|
|
@ -15,6 +15,8 @@ export function timelineComputations (store) {
|
|||
computeForTimeline(store, 'lastFocusedElementSelector')
|
||||
computeForTimeline(store, 'ignoreBlurEvents')
|
||||
computeForTimeline(store, 'itemIdsToAdd')
|
||||
computeForTimeline(store, 'showHeader')
|
||||
computeForTimeline(store, 'shouldShowHeader')
|
||||
|
||||
store.compute('firstTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds && timelineItemIds.length && timelineItemIds[0])
|
||||
store.compute('lastTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds && timelineItemIds.length && timelineItemIds[timelineItemIds.length - 1])
|
||||
|
|
Loading…
Reference in New Issue