<:Window bind:online /> <div class="timeline" role="feed" aria-label="{{label}}" on:initialized> <VirtualList component="{{StatusListItem}}" :makeProps items="{{statusIds}}" 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, statusIds: [], runningUpdate: false, initialized: false }), 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}` } else if (timeline.startsWith('tag/')) { let tag = timeline.split('/').slice(-1)[0] return `#${tag} timeline for ${$currentInstance}` } else if (timeline.startsWith('account/')) { let account = timeline.split('/').slice(-1)[0] return `Account #${account} on ${$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 instanceName = this.store.get('instanceName') let timeline = this.get('timeline') /* no await */ database.insertStatuses(instanceName, timeline, newStatuses) let statusIds = this.get('statusIds') if (!statusIds) { return } let newStatusIds = newStatuses.map(status => status.id) let merged = mergeStatuses(statusIds, newStatusIds) this.set({ 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 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>