diff --git a/routes/_actions/streaming.js b/routes/_actions/streaming.js
index 5fc7b2ee..ba869173 100644
--- a/routes/_actions/streaming.js
+++ b/routes/_actions/streaming.js
@@ -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)
}
})
-}
+}
\ No newline at end of file
diff --git a/routes/_actions/timeline.js b/routes/_actions/timeline.js
index a17596fb..e5a07628 100644
--- a/routes/_actions/timeline.js
+++ b/routes/_actions/timeline.js
@@ -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
+ })
+}
\ No newline at end of file
diff --git a/routes/_components/timeline/MoreHeader.html b/routes/_components/timeline/MoreHeader.html
new file mode 100644
index 00000000..849db49e
--- /dev/null
+++ b/routes/_components/timeline/MoreHeader.html
@@ -0,0 +1,25 @@
+
+
+
\ No newline at end of file
diff --git a/routes/_components/timeline/MoreHeaderVirtualWrapper.html b/routes/_components/timeline/MoreHeaderVirtualWrapper.html
new file mode 100644
index 00000000..9043c441
--- /dev/null
+++ b/routes/_components/timeline/MoreHeaderVirtualWrapper.html
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/routes/_components/timeline/Timeline.html b/routes/_components/timeline/Timeline.html
index b4d72814..db478be6 100644
--- a/routes/_components/timeline/Timeline.html
+++ b/routes/_components/timeline/Timeline.html
@@ -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})
diff --git a/routes/_components/virtualList/VirtualList.html b/routes/_components/virtualList/VirtualList.html
index 66914561..81b21ee7 100644
--- a/routes/_components/virtualList/VirtualList.html
+++ b/routes/_components/virtualList/VirtualList.html
@@ -1,5 +1,5 @@
-
+
{{#if $visibleItems}}
{{#each $visibleItems as visibleItem @key}}
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
}
diff --git a/routes/_components/virtualList/VirtualListHeader.html b/routes/_components/virtualList/VirtualListHeader.html
new file mode 100644
index 00000000..37ae79e2
--- /dev/null
+++ b/routes/_components/virtualList/VirtualListHeader.html
@@ -0,0 +1,34 @@
+
+
+
\ No newline at end of file
diff --git a/routes/_components/virtualList/virtualListStore.js b/routes/_components/virtualList/virtualListStore.js
index bba41a68..23bcf3dc 100644
--- a/routes/_components/virtualList/virtualListStore.js
+++ b/routes/_components/virtualList/virtualListStore.js
@@ -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) {
diff --git a/routes/_store/timelineComputations.js b/routes/_store/timelineComputations.js
index 0e7bbe87..0eb37a6b 100644
--- a/routes/_store/timelineComputations.js
+++ b/routes/_store/timelineComputations.js
@@ -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])