From f7164dd4c0201ec8473a9cc32a5aa9a1a30abcbe Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Wed, 5 Dec 2018 00:21:54 -0800 Subject: [PATCH] fix(observers): refactor instance observers, minor optimizations (#730) try to defer more work, split everything up into more functional code for easier reading --- routes/_actions/lists.js | 13 +- routes/_pages/community/index.html | 6 +- routes/_store/observers/instanceObservers.js | 150 +++++++++++-------- 3 files changed, 97 insertions(+), 72 deletions(-) diff --git a/routes/_actions/lists.js b/routes/_actions/lists.js index 9aa6bc58..d903273b 100644 --- a/routes/_actions/lists.js +++ b/routes/_actions/lists.js @@ -3,16 +3,17 @@ import { getLists } from '../_api/lists' import { cacheFirstUpdateAfter } from '../_utils/sync' import { database } from '../_database/database' -export async function updateLists () { - let { currentInstance, accessToken } = store.get() +export async function updateListsForInstance (instanceName) { + let { loggedInInstances } = store.get() + let accessToken = loggedInInstances[instanceName].access_token await cacheFirstUpdateAfter( - () => getLists(currentInstance, accessToken), - () => database.getLists(currentInstance), - lists => database.setLists(currentInstance, lists), + () => getLists(instanceName, accessToken), + () => database.getLists(instanceName), + lists => database.setLists(instanceName, lists), lists => { let { instanceLists } = store.get() - instanceLists[currentInstance] = lists + instanceLists[instanceName] = lists store.set({ instanceLists: instanceLists }) } ) diff --git a/routes/_pages/community/index.html b/routes/_pages/community/index.html index 8ba8932e..ef55ce1d 100644 --- a/routes/_pages/community/index.html +++ b/routes/_pages/community/index.html @@ -92,13 +92,13 @@ import HiddenFromSSR from '../../_components/HiddenFromSSR' import PageList from '../../_components/community/PageList.html' import PageListItem from '../../_components/community/PageListItem.html' - import { updateLists } from '../../_actions/lists' + import { updateListsForInstance } from '../../_actions/lists' export default { async oncreate () { let { currentInstance } = this.store.get() if (currentInstance) { - await updateLists() + await updateListsForInstance(currentInstance) } }, store: () => store, @@ -112,4 +112,4 @@ isLockedAccount: ({ $currentVerifyCredentials }) => $currentVerifyCredentials && $currentVerifyCredentials.locked } } - \ No newline at end of file + diff --git a/routes/_store/observers/instanceObservers.js b/routes/_store/observers/instanceObservers.js index aaa8dc89..1e63f409 100644 --- a/routes/_store/observers/instanceObservers.js +++ b/routes/_store/observers/instanceObservers.js @@ -1,16 +1,98 @@ import { updateInstanceInfo, updateVerifyCredentialsForInstance } from '../../_actions/instances' -import { updateLists } from '../../_actions/lists' +import { updateListsForInstance } from '../../_actions/lists' import { createStream } from '../../_actions/streaming' import { updatePushSubscriptionForInstance } from '../../_actions/pushSubscription' import { updateCustomEmojiForInstance } from '../../_actions/emoji' import { addStatusesOrNotifications } from '../../_actions/addStatusOrNotification' import { getTimeline } from '../../_api/timelines' import { TIMELINE_BATCH_SIZE } from '../../_static/timelines' +import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' +import { mark, stop } from '../../_utils/marks' + +// stream to watch for home timeline updates and notifications +let currentInstanceStream + +async function refreshInstanceDataAndStream (store, instanceName) { + mark(`refreshInstanceDataAndStream-${instanceName}`) + await doRefreshInstanceDataAndStream(store, instanceName) + stop(`refreshInstanceDataAndStream-${instanceName}`) +} + +function currentInstanceChanged (store, instanceName) { + return store.get().currentInstance !== instanceName +} + +async function doRefreshInstanceDataAndStream (store, instanceName) { + if (currentInstanceChanged(store, instanceName)) { + return + } + + await refreshInstanceData(instanceName) + + if (currentInstanceChanged(store, instanceName)) { + return + } + + let { currentInstanceInfo } = store.get() + if (!currentInstanceInfo) { + return + } + + stream(store, instanceName, currentInstanceInfo) +} + +async function refreshInstanceData (instanceName) { + // these are all low-priority + scheduleIdleTask(() => updateVerifyCredentialsForInstance(instanceName)) + scheduleIdleTask(() => updateCustomEmojiForInstance(instanceName)) + scheduleIdleTask(() => updateListsForInstance(instanceName)) + scheduleIdleTask(() => updatePushSubscriptionForInstance(instanceName)) + + // this is the only critical one + await updateInstanceInfo(instanceName) +} + +function stream (store, instanceName, currentInstanceInfo) { + let homeTimelineItemIds = store.getForTimeline(instanceName, + 'home', 'timelineItemIds') + let firstHomeTimelineItemId = homeTimelineItemIds && homeTimelineItemIds[0] + let notificationItemIds = store.getForTimeline(instanceName, + 'notifications', 'timelineItemIds') + let firstNotificationTimelineItemId = notificationItemIds && notificationItemIds[0] + + let { accessToken } = store.get() + let streamingApi = currentInstanceInfo.urls.streaming_api + + function onOpenStream () { + if (currentInstanceChanged(store, instanceName)) { + return + } + + /* no await */ fillGap(instanceName, accessToken, 'home', firstHomeTimelineItemId) + /* no await */ fillGap(instanceName, accessToken, 'notifications', firstNotificationTimelineItemId) + } + + currentInstanceStream = createStream(streamingApi, instanceName, accessToken, 'home', onOpenStream) + + if (process.env.NODE_ENV !== 'production') { + window.currentInstanceStream = currentInstanceStream + } +} + +// 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 +async function fillGap (instanceName, accessToken, timelineName, firstTimelineItemId) { + if (!firstTimelineItemId) { + return + } + let newTimelineItems = await getTimeline(instanceName, accessToken, + timelineName, null, firstTimelineItemId, TIMELINE_BATCH_SIZE) + if (newTimelineItems.length) { + addStatusesOrNotifications(instanceName, timelineName, newTimelineItems) + } +} export function instanceObservers (store) { - // stream to watch for home timeline updates and notifications - let currentInstanceStream - store.observe('currentInstance', async (currentInstance) => { if (!process.browser) { return @@ -26,64 +108,6 @@ export function instanceObservers (store) { return } - /* no await */ updateVerifyCredentialsForInstance(currentInstance) - /* no await */ updateCustomEmojiForInstance(currentInstance) - /* no await */ updateLists() - /* no await */ updatePushSubscriptionForInstance(currentInstance) - await updateInstanceInfo(currentInstance) - - let currentInstanceIsUnchanged = () => { - let { currentInstance: newCurrentInstance } = store.get() - return newCurrentInstance === currentInstance - } - - if (!currentInstanceIsUnchanged()) { - return - } - - let { currentInstanceInfo } = store.get() - if (!currentInstanceInfo) { - return - } - - let homeTimelineItemIds = store.getForTimeline(currentInstance, - 'home', 'timelineItemIds') - let firstHomeTimelineItemId = homeTimelineItemIds && homeTimelineItemIds[0] - let notificationItemIds = store.getForTimeline(currentInstance, - 'notifications', 'timelineItemIds') - let firstNotificationTimelineItemId = notificationItemIds && notificationItemIds[0] - - let onOpenStream = async () => { - if (!currentInstanceIsUnchanged()) { - return - } - - // 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 - async function fillGap (timelineName, firstTimelineItemId) { - if (!firstTimelineItemId) { - return - } - let newTimelineItems = await getTimeline(currentInstance, accessToken, - timelineName, null, firstTimelineItemId, TIMELINE_BATCH_SIZE) - if (newTimelineItems.length) { - addStatusesOrNotifications(currentInstance, timelineName, newTimelineItems) - } - } - - await Promise.all([ - fillGap('home', firstHomeTimelineItemId), - fillGap('notifications', firstNotificationTimelineItemId) - ]) - } - - let { accessToken } = store.get() - let streamingApi = currentInstanceInfo.urls.streaming_api - currentInstanceStream = createStream(streamingApi, - currentInstance, accessToken, 'home', onOpenStream) - - if (process.env.NODE_ENV !== 'production') { - window.currentInstanceStream = currentInstanceStream - } + scheduleIdleTask(() => refreshInstanceDataAndStream(store, currentInstance)) }) }