<:Window bind:online /> <div class="timeline" role="feed" aria-label="{{label}}" on:initialized> <VirtualList component="{{StatusListItem}}" items="{{keyedStatuses}}" on:scrollToBottom="onScrollToBottom()" shown="{{initialized}}" footerComponent="{{LoadingFooter}}" showFooter="{{initialized && runningUpdate}}" /> </div> <style> .timeline { min-height: 60vh; } </style> <script> import { store } from '../_utils/store' import { getTimeline } from '../_utils/mastodon/timelines' import StatusListItem from './StatusListItem.html' import LoadingFooter from './LoadingFooter.html' import VirtualList from './VirtualList.html' import { splice, push } from 'svelte-extras' import { mergeStatuses } from '../_utils/statuses' import { mark, stop } from '../_utils/marks' import { timelines } from '../_static/timelines' import { toast } from '../_utils/toast' import { database } from '../_utils/database/database' const FETCH_LIMIT = 20 export default { async oncreate() { let statuses = await this.fetchStatusesAndPossiblyFallBack() this.addStatuses(statuses) requestAnimationFrame(() => { requestAnimationFrame((() => { this.set({initialized: true}) this.fire('initialized') })) }) }, data: () => ({ StatusListItem: StatusListItem, LoadingFooter: LoadingFooter, statuses: [], runningUpdate: false, initialized: false }), computed: { keyedStatuses: (statuses) => statuses.map(status => ({ props: status, key: status.id })), lastStatusId: (statuses) => statuses.length && statuses[statuses.length - 1].id, label: (timeline, $currentInstance) => { if (timelines[timeline]) { `${timelines[timeline].label} timeline for ${$currentInstance}` } else if (timeline.startsWith('tag/')) { let tag = timeline.split('/').slice(-1)[0] return `#${tag} timeline for ${$currentInstance}` } } }, store: () => store, components: { VirtualList }, methods: { splice: splice, push: push, async onScrollToBottom() { if (!this.get('initialized')) { return } if (this.get('runningUpdate')) { return } mark('onScrollToBottom') this.set({ runningUpdate: true }) let newStatuses = await this.fetchStatusesAndPossiblyFallBack() this.set({ runningUpdate: false }) this.addStatuses(newStatuses) stop('onScrollToBottom') }, addStatuses(newStatuses) { if (process.env.NODE_ENV !== 'production') { console.log('addStatuses()') } let statuses = this.get('statuses') if (!statuses) { return } let merged = mergeStatuses(statuses, newStatuses) this.set({ statuses: 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 statuses if (!online) { statuses = await database.getTimeline(instanceName, timeline, lastStatusId, FETCH_LIMIT) } else { try { statuses = await getTimeline(instanceName, instanceData.access_token, timeline, lastStatusId, FETCH_LIMIT) /* no await */ database.insertStatuses(instanceName, timeline, statuses) } catch (e) { console.error(e) toast.say('Internet request failed. Showing offline content.') statuses = await database.getTimeline(instanceName, timeline, lastStatusId, FETCH_LIMIT) } } return statuses } } } </script>