move all timeline data to centralized store

This commit is contained in:
Nolan Lawson 2018-01-27 10:46:52 -08:00
parent 9782cb400f
commit 5f627bf9f1
5 changed files with 62 additions and 39 deletions

View file

@ -1,7 +1,7 @@
<Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/>
{{#if virtual}}
<VirtualListContainer realm="{{virtualRealm}}">
<VirtualListContainer realm="{{$currentInstance + '/' + virtualRealm}}">
<main>
<slot></slot>
</main>
@ -16,11 +16,13 @@
<script>
import Nav from './Nav.html';
import VirtualListContainer from './virtualList/VirtualListContainer.html'
import { store } from '../_utils/store'
export default {
components: {
VirtualListContainer,
Nav
}
},
store: () => store
}
</script>

View file

@ -1,5 +1,5 @@
<div class="lazy-timeline">
{{#if loading}}
{{#if !$initialized}}
<div transition:fade>
<div class="loading-page">
<LoadingSpinner />
@ -8,7 +8,7 @@
{{/if}}
{{#await promise}}
{{then constructor}}
<:Component {constructor} :timeline on:initialized="set({'loading': false})"/>
<:Component {constructor} :timeline />
{{catch error}}
<div>Component failed to load. Please try refreshing! {{error}}</div>
{{/await}}
@ -34,11 +34,18 @@
import { importTimeline } from '../_utils/asyncModules'
import LoadingSpinner from './LoadingSpinner.html'
import { fade } from 'svelte-transitions'
import { store } from '../_utils/store'
export default {
oncreate() {
let instanceName = this.store.get('currentInstance')
let timeline = this.get('timeline')
this.store.set({currentTimeline: timeline})
this.store.setForTimeline(instanceName, timeline, {runningUpdate: false})
},
store: () => store,
data: () => ({
promise: importTimeline(),
loading: true
promise: importTimeline()
}),
components: {
LoadingSpinner

View file

@ -1,13 +1,13 @@
<:Window bind:online />
<div class="timeline" role="feed" aria-label="{{label}}" on:initialized>
<div class="timeline" role="feed" aria-label="{{label}}">
<VirtualList component="{{StatusListItem}}"
:makeProps
items="{{statusIds}}"
items="{{$statusIds}}"
on:scrollToBottom="onScrollToBottom()"
shown="{{initialized}}"
shown="{{$initialized}}"
footerComponent="{{LoadingFooter}}"
showFooter="{{initialized && runningUpdate}}"
realm="{{timeline}}"
showFooter="{{$initialized && $runningUpdate}}"
realm="{{$currentInstance + '/' + timeline}}"
on:initializedVisibleItems="initialize()"
/>
</div>
@ -31,12 +31,6 @@
import { database } from '../_utils/database/database'
import { StatusStream } from '../_utils/mastodon/StatusStream'
const cachedTimelines = {}
if (process.browser && process.env.NODE_ENV !== 'production') {
window.cachedTimelines = cachedTimelines
}
const FETCH_LIMIT = 20
export default {
@ -44,10 +38,7 @@
let timeline = this.get('timeline')
let instanceName = this.store.get('currentInstance')
let accessToken = this.store.get('accessToken')
let cachedStatusIds = cachedTimelines[timeline]
if (cachedStatusIds) {
this.set({statusIds: cachedStatusIds})
} else {
if (!this.store.get('statusIds').length) {
this.addStatuses(await this.fetchStatusesAndPossiblyFallBack())
}
/* no await */ getInstanceInfo(instanceName).then(instanceInfo => database.setInstanceInfo(instanceName, instanceInfo))
@ -62,18 +53,13 @@
if (this._statusStream) {
this._statusStream.close()
}
cachedTimelines[this.get('timeline')] = this.get('statusIds')
},
data: () => ({
StatusListItem: StatusListItem,
LoadingFooter: LoadingFooter,
statusIds: [],
runningUpdate: false,
initialized: false
LoadingFooter: LoadingFooter
}),
computed: {
makeProps: ($currentInstance) => (statusId) => database.getStatus($currentInstance, statusId),
lastStatusId: (statusIds) => statusIds.length && statusIds[statusIds.length - 1],
label: (timeline, $currentInstance) => {
if (timelines[timeline]) {
`${timelines[timeline].label} timeline for ${$currentInstance}`
@ -94,27 +80,30 @@
splice: splice,
push: push,
initialize() {
if (this.get('initialized') || !this.get('statusIds') || !this.get('statusIds').length) {
if (this.store.get('initialized') || !this.store.get('statusIds') || !this.store.get('statusIds').length) {
return
}
let instanceName = this.store.get('currentInstance')
let timeline = this.get('timeline')
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.set({initialized: true})
this.fire('initialized')
this.store.setForTimeline(instanceName, timeline, {initialized: true})
})
})
},
async onScrollToBottom() {
if (!this.get('initialized')) {
if (!this.store.get('initialized')) {
return
}
if (this.get('runningUpdate')) {
if (this.store.get('runningUpdate')) {
return
}
mark('onScrollToBottom')
this.set({ runningUpdate: true })
let timeline = this.get('timeline')
let instanceName = this.store.get('currentInstance')
this.store.setForTimeline(instanceName, timeline, { runningUpdate: true })
let newStatuses = await this.fetchStatusesAndPossiblyFallBack()
this.set({ runningUpdate: false })
this.store.setForTimeline(instanceName, timeline, { runningUpdate: false })
this.addStatuses(newStatuses)
stop('onScrollToBottom')
},
@ -122,21 +111,21 @@
console.log('addStatuses()')
let instanceName = this.store.get('currentInstance')
let timeline = this.get('timeline')
let statusIds = this.get('statusIds')
let statusIds = this.store.get('statusIds')
if (!statusIds) {
return
}
/* no await */ database.insertStatuses(instanceName, timeline, newStatuses)
let newStatusIds = newStatuses.map(status => status.id)
let merged = mergeStatuses(statusIds, newStatusIds)
this.set({ statusIds: merged })
this.store.setForTimeline(instanceName, timeline, { statusIds: merged })
},
async fetchStatusesAndPossiblyFallBack() {
let online = this.get('online')
let instanceName = this.store.get('currentInstance')
let instanceData = this.store.get('currentInstanceData')
let timeline = this.get('timeline')
let lastStatusId = this.get('lastStatusId')
let lastStatusId = this.store.get('lastStatusId')
let statuses
if (!online) {
statuses = await database.getTimeline(instanceName, timeline, lastStatusId, FETCH_LIMIT)

View file

@ -14,6 +14,9 @@ class AsyncLayout {
}
observe(key, node, callback) {
if (!node) {
return
}
if (this._intersectionObserver) {
this._onIntersectionCallbacks[key] = (entry) => {
callback(getRectFromEntry(entry))
@ -27,6 +30,9 @@ class AsyncLayout {
if (key in this._onIntersectionCallbacks) {
return
}
if (!node) {
return
}
if (this._intersectionObserver) {
this._intersectionObserver.unobserve(node)
}

View file

@ -11,7 +11,7 @@ const LOCAL_STORAGE_KEYS = new Set([
])
const LS = process.browser && localStorage
class LocalStorageStore extends Store {
class PinaforeStore extends Store {
constructor(state) {
super(state)
@ -44,9 +44,18 @@ class LocalStorageStore extends Store {
this.keysToStore = {}
}
}
setForTimeline(instanceName, timelineName, obj) {
console.log('setForTimeline')
let timelines = this.get('timelines') || {}
let timelineData = timelines[instanceName] || {}
timelineData[timelineName] = Object.assign(timelineData[timelineName] || {}, obj)
timelines[instanceName] = timelineData
this.set({timelines: timelines})
}
}
const store = new LocalStorageStore({
const store = new PinaforeStore({
instanceNameInSearch: '',
currentRegisteredInstance: null,
currentRegisteredInstanceName: '',
@ -98,6 +107,16 @@ store.compute(
}
)
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
(currentInstance, currentTimeline, timelines) => {
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
})
store.compute('statusIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.statusIds || [])
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
store.compute('lastStatusId', ['statusIds'], (statusIds) => statusIds.length && statusIds[statusIds.length - 1])
if (process.browser && process.env.NODE_ENV !== 'production') {
window.store = store // for debugging
}