fix streaming gap issue

This commit is contained in:
Nolan Lawson 2018-03-19 10:09:05 -07:00
parent add39a7334
commit eaa19f79e4
4 changed files with 83 additions and 15 deletions

View file

@ -76,8 +76,12 @@ const lazilyProcessFreshUpdates = throttle((instanceName, timelineName) => {
}, 5000) }, 5000)
export function addStatusOrNotification (instanceName, timelineName, newStatusOrNotification) { export function addStatusOrNotification (instanceName, timelineName, newStatusOrNotification) {
addStatusesOrNotifications(instanceName, timelineName, [newStatusOrNotification])
}
export function addStatusesOrNotifications (instanceName, timelineName, newStatusesOrNotifications) {
let freshUpdates = store.getForTimeline(instanceName, timelineName, 'freshUpdates') || [] let freshUpdates = store.getForTimeline(instanceName, timelineName, 'freshUpdates') || []
freshUpdates.push(newStatusOrNotification) freshUpdates = freshUpdates.concat(newStatusesOrNotifications)
freshUpdates = uniqBy(freshUpdates, _ => _.id) freshUpdates = uniqBy(freshUpdates, _ => _.id)
store.setForTimeline(instanceName, timelineName, {freshUpdates: freshUpdates}) store.setForTimeline(instanceName, timelineName, {freshUpdates: freshUpdates})
lazilyProcessFreshUpdates(instanceName, timelineName) lazilyProcessFreshUpdates(instanceName, timelineName)

View file

@ -1,15 +1,14 @@
import { updateInstanceInfo } from '../../_actions/instances' import { updateInstanceInfo } from '../../_actions/instances'
import { createStream } from '../../_actions/streaming' import { createStream } from '../../_actions/streaming'
import { getTimeline } from '../../_api/timelines'
import { addStatusesOrNotifications } from '../../_actions/addStatusOrNotification'
export function timelineObservers (store) { export function timelineObservers (store) {
// stream to watch for local/federated/etc. updates. home and notification // stream to watch for local/federated/etc. updates. home and notification
// updates are handled in timelineObservers.js // updates are handled in timelineObservers.js
let currentTimelineStream let currentTimelineStream
store.observe('currentTimeline', async (currentTimeline) => { function shutdownPreviousStream () {
if (!process.browser) {
return
}
if (currentTimelineStream) { if (currentTimelineStream) {
currentTimelineStream.close() currentTimelineStream.close()
currentTimelineStream = null currentTimelineStream = null
@ -17,26 +16,61 @@ export function timelineObservers (store) {
window.currentTimelineStream = null window.currentTimelineStream = null
} }
} }
if (!currentTimeline) { }
function shouldObserveTimeline (timeline) {
return timeline &&
!(
timeline !== 'local' &&
timeline !== 'federated' &&
!timeline.startsWith('list/') &&
!timeline.startsWith('tag/')
)
}
store.observe('currentTimeline', async (currentTimeline) => {
if (!process.browser) {
return return
} }
if (currentTimeline !== 'local' &&
currentTimeline !== 'federated' && shutdownPreviousStream()
!currentTimeline.startsWith('list/') &&
!currentTimeline.startsWith('tag/')) { if (!shouldObserveTimeline(currentTimeline)) {
return return
} }
let currentInstance = store.get('currentInstance') let currentInstance = store.get('currentInstance')
let accessToken = store.get('accessToken')
await updateInstanceInfo(currentInstance) await updateInstanceInfo(currentInstance)
let instanceInfo = store.get('currentInstanceInfo')
if (!(instanceInfo && let checkInstanceAndTimelineAreUnchanged = () => (
store.get('currentInstance') === currentInstance && store.get('currentInstance') === currentInstance &&
store.get('currentTimeline') === currentTimeline)) { store.get('currentTimeline') === currentTimeline
)
if (!checkInstanceAndTimelineAreUnchanged()) {
return return
} }
let accessToken = store.get('accessToken') let timelineItemIds = store.getForTimeline(currentInstance,
currentTimeline, 'timelineItemIds')
let firstTimelineItemId = timelineItemIds && timelineItemIds[0]
if (firstTimelineItemId) {
// fill in the "streaming gap" i.e. fetch the most recent 20 items so that there isn't
// a big gap in the timeline if you haven't looked at it in awhile
// TODO: race condition here, should start streaming while this request is ongoing
let newTimelineItems = await getTimeline(currentInstance, accessToken, currentTimeline,
null, firstTimelineItemId)
if (newTimelineItems.length) {
addStatusesOrNotifications(currentInstance, currentTimeline, newTimelineItems)
}
if (!checkInstanceAndTimelineAreUnchanged()) {
return
}
}
let instanceInfo = store.get('currentInstanceInfo')
currentTimelineStream = createStream(instanceInfo.urls.streaming_api, currentTimelineStream = createStream(instanceInfo.urls.streaming_api,
currentInstance, accessToken, currentTimeline) currentInstance, accessToken, currentTimeline)

View file

@ -0,0 +1,29 @@
import { foobarRole } from '../roles'
import {
getNthStatus, homeNavButton, localTimelineNavButton, sleep
} from '../utils'
import {
postAs
} from '../serverActions'
fixture`107-streaming-gap.js`
.page`http://localhost:4002`
test('fills in a status posted while away from timeline', async t => {
let timeout = 20000
await t.useRole(foobarRole)
.click(localTimelineNavButton)
.hover(getNthStatus(0))
await postAs('admin', 'heyo')
await t.expect(getNthStatus(0).innerText).contains('heyo', {timeout})
.click(homeNavButton)
.hover(getNthStatus(0))
await postAs('admin', 'posted this while you were away!')
await t.expect(getNthStatus(0).innerText).contains('posted this while you were away!', {timeout})
.click(localTimelineNavButton)
.expect(getNthStatus(0).innerText).contains('posted this while you were away!', {timeout})
await sleep(2000)
await postAs('admin', 'posted this while you were watching')
await t.expect(getNthStatus(0).innerText).contains('posted this while you were watching', {timeout})
})

View file

@ -10,6 +10,7 @@ export const modalDialogContents = $('.modal-dialog-contents')
export const closeDialogButton = $('.close-dialog-button') export const closeDialogButton = $('.close-dialog-button')
export const notificationsNavButton = $('nav a[href="/notifications"]') export const notificationsNavButton = $('nav a[href="/notifications"]')
export const homeNavButton = $('nav a[href="/"]') export const homeNavButton = $('nav a[href="/"]')
export const localTimelineNavButton = $('nav a[href="/local"]')
export const searchNavButton = $('nav a[href="/search"]') export const searchNavButton = $('nav a[href="/search"]')
export const formError = $('.form-error-user-error') export const formError = $('.form-error-user-error')
export const composeInput = $('.compose-box-input') export const composeInput = $('.compose-box-input')