add ability to fetch and store notifications
This commit is contained in:
		
							parent
							
								
									43170b9f6f
								
							
						
					
					
						commit
						c8cb4354e3
					
				
					 11 changed files with 166 additions and 98 deletions
				
			
		| 
						 | 
				
			
			@ -2,55 +2,51 @@ import { store } from '../_store/store'
 | 
			
		|||
import { database } from '../_utils/database/database'
 | 
			
		||||
import { getTimeline } from '../_utils/mastodon/timelines'
 | 
			
		||||
import { toast } from '../_utils/toast'
 | 
			
		||||
import { StatusStream } from '../_utils/mastodon/StatusStream'
 | 
			
		||||
import { getInstanceInfo } from '../_utils/mastodon/instance'
 | 
			
		||||
import { mark, stop } from '../_utils/marks'
 | 
			
		||||
import { mergeArrays } from '../_utils/arrays'
 | 
			
		||||
 | 
			
		||||
const FETCH_LIMIT = 20
 | 
			
		||||
 | 
			
		||||
let statusStream
 | 
			
		||||
 | 
			
		||||
async function fetchStatuses(instanceName, accessToken, timelineName, lastStatusId, online) {
 | 
			
		||||
  mark('fetchStatuses')
 | 
			
		||||
  let statuses
 | 
			
		||||
async function fetchTimelineItems(instanceName, accessToken, timelineName, lastTimelineItemId, online) {
 | 
			
		||||
  mark('fetchTimelineItems')
 | 
			
		||||
  let items
 | 
			
		||||
  if (!online) {
 | 
			
		||||
    statuses = await database.getTimeline(instanceName, timelineName, lastStatusId, FETCH_LIMIT)
 | 
			
		||||
    items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
 | 
			
		||||
  } else {
 | 
			
		||||
    try {
 | 
			
		||||
      statuses = await getTimeline(instanceName, accessToken, timelineName, lastStatusId, FETCH_LIMIT)
 | 
			
		||||
      /* no await */ database.insertStatuses(instanceName, timelineName, statuses)
 | 
			
		||||
      items = await getTimeline(instanceName, accessToken, timelineName, lastTimelineItemId, FETCH_LIMIT)
 | 
			
		||||
      /* no await */ database.insertTimelineItems(instanceName, timelineName, items)
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e)
 | 
			
		||||
      toast.say('Internet request failed. Showing offline content.')
 | 
			
		||||
      statuses = await database.getTimeline(instanceName, timelineName, lastStatusId, FETCH_LIMIT)
 | 
			
		||||
      items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  stop('fetchStatuses')
 | 
			
		||||
  return statuses
 | 
			
		||||
  stop('fetchTimelineItems')
 | 
			
		||||
  return items
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function addStatuses(instanceName, timelineName, newStatuses) {
 | 
			
		||||
  console.log('addStatuses, length:', newStatuses.length)
 | 
			
		||||
  mark('addStatuses')
 | 
			
		||||
  let newStatusIds = newStatuses.map(status => status.id)
 | 
			
		||||
  let oldStatusIds = store.getForTimeline(instanceName, timelineName, 'statusIds') || []
 | 
			
		||||
  let merged = mergeArrays(oldStatusIds, newStatusIds)
 | 
			
		||||
  store.setForTimeline(instanceName, timelineName, { statusIds: merged })
 | 
			
		||||
  stop('addStatuses')
 | 
			
		||||
async function addTimelineItems(instanceName, timelineName, newItems) {
 | 
			
		||||
  console.log('addTimelineItems, length:', newItems.length)
 | 
			
		||||
  mark('addTimelineItems')
 | 
			
		||||
  let newIds = newItems.map(item => item.id)
 | 
			
		||||
  let oldIds = store.getForTimeline(instanceName, timelineName, 'timelineItemIds') || []
 | 
			
		||||
  let merged = mergeArrays(oldIds, newIds)
 | 
			
		||||
  store.setForTimeline(instanceName, timelineName, { timelineItemIds: merged })
 | 
			
		||||
  stop('addTimelineItems')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function fetchStatusesAndPossiblyFallBack() {
 | 
			
		||||
  mark('fetchStatusesAndPossiblyFallBack')
 | 
			
		||||
async function fetchTimelineItemsAndPossiblyFallBack() {
 | 
			
		||||
  mark('fetchTimelineItemsAndPossiblyFallBack')
 | 
			
		||||
  let timelineName = store.get('currentTimeline')
 | 
			
		||||
  let instanceName = store.get('currentInstance')
 | 
			
		||||
  let accessToken = store.get('accessToken')
 | 
			
		||||
  let lastStatusId = store.get('lastStatusId')
 | 
			
		||||
  let lastTimelineItemId = store.get('lastTimelineItemId')
 | 
			
		||||
  let online = store.get('online')
 | 
			
		||||
 | 
			
		||||
  let newStatuses = await fetchStatuses(instanceName, accessToken, timelineName, lastStatusId, online)
 | 
			
		||||
  addStatuses(instanceName, timelineName, newStatuses)
 | 
			
		||||
  stop('fetchStatusesAndPossiblyFallBack')
 | 
			
		||||
  let newItems = await fetchTimelineItems(instanceName, accessToken, timelineName, lastTimelineItemId, online)
 | 
			
		||||
  addTimelineItems(instanceName, timelineName, newItems)
 | 
			
		||||
  stop('fetchTimelineItemsAndPossiblyFallBack')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function initializeTimeline() {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,30 +62,20 @@ export function initializeTimeline() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export async function setupTimeline() {
 | 
			
		||||
  mark('addStatuses')
 | 
			
		||||
  mark('setupTimeline')
 | 
			
		||||
  let timelineName = store.get('currentTimeline')
 | 
			
		||||
  let instanceName = store.get('currentInstance')
 | 
			
		||||
  let accessToken = store.get('accessToken')
 | 
			
		||||
  if (!store.get('statusIds').length) {
 | 
			
		||||
    await fetchStatusesAndPossiblyFallBack()
 | 
			
		||||
  if (!store.get('timelineItemIds').length) {
 | 
			
		||||
    await fetchTimelineItemsAndPossiblyFallBack()
 | 
			
		||||
  }
 | 
			
		||||
  /* no await */ getInstanceInfo(instanceName).then(instanceInfo => database.setInstanceInfo(instanceName, instanceInfo))
 | 
			
		||||
  let instanceInfo = await database.getInstanceInfo(instanceName)
 | 
			
		||||
  if (statusStream) {
 | 
			
		||||
    statusStream.close()
 | 
			
		||||
  }
 | 
			
		||||
  /*statusStream = new StatusStream(instanceInfo.urls.streaming_api, accessToken, timelineName, {
 | 
			
		||||
    onMessage(message) {
 | 
			
		||||
      console.log('message', message)
 | 
			
		||||
    }
 | 
			
		||||
  })*/
 | 
			
		||||
  stop('addStatuses')
 | 
			
		||||
  stop('setupTimeline')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function fetchStatusesOnScrollToBottom() {
 | 
			
		||||
export async function fetchTimelineItemsOnScrollToBottom() {
 | 
			
		||||
  let timelineName = store.get('currentTimeline')
 | 
			
		||||
  let instanceName = store.get('currentInstance')
 | 
			
		||||
  store.setForTimeline(instanceName, timelineName, { runningUpdate: true })
 | 
			
		||||
  await fetchStatusesAndPossiblyFallBack()
 | 
			
		||||
  await fetchTimelineItemsAndPossiblyFallBack()
 | 
			
		||||
  store.setForTimeline(instanceName, timelineName, { runningUpdate: false })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								routes/_components/notification/Notification.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								routes/_components/notification/Notification.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
<article class="notification-article"
 | 
			
		||||
         tabindex="0"
 | 
			
		||||
         aria-posinset="{{index}}" aria-setsize="{{length}}"
 | 
			
		||||
         on:recalculateHeight
 | 
			
		||||
>
 | 
			
		||||
  Notification
 | 
			
		||||
</article>
 | 
			
		||||
							
								
								
									
										16
									
								
								routes/_components/timeline/NotificationVirtualListItem.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								routes/_components/timeline/NotificationVirtualListItem.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
<Notification
 | 
			
		||||
  notification="{{virtualProps.notification}}"
 | 
			
		||||
  timelineType="{{virtualProps.timelineType}}"
 | 
			
		||||
  timelineValue="{{virtualProps.timelineValue}}"
 | 
			
		||||
  index="{{virtualIndex}}"
 | 
			
		||||
  length="{{virtualLength}}"
 | 
			
		||||
  on:recalculateHeight />
 | 
			
		||||
<script>
 | 
			
		||||
  import Notification from '../notification/Notification.html'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    components: {
 | 
			
		||||
      Notification
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -4,10 +4,21 @@
 | 
			
		|||
      <LoadingSpinner />
 | 
			
		||||
    </div>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  {{#if virtual}}
 | 
			
		||||
  {{#if timelineType === 'notifications'}}
 | 
			
		||||
    <VirtualList component="{{NotificationVirtualListItem}}"
 | 
			
		||||
                 :makeProps
 | 
			
		||||
                 items="{{$timelineItemIds}}"
 | 
			
		||||
                 on:scrollToBottom="onScrollToBottom()"
 | 
			
		||||
                 shown="{{$initialized}}"
 | 
			
		||||
                 footerComponent="{{LoadingFooter}}"
 | 
			
		||||
                 showFooter="{{$initialized && $runningUpdate}}"
 | 
			
		||||
                 realm="{{$currentInstance + '/' + timeline}}"
 | 
			
		||||
                 on:initializedVisibleItems="initialize()"
 | 
			
		||||
  />
 | 
			
		||||
  {{elseif virtual}}
 | 
			
		||||
    <VirtualList component="{{StatusVirtualListItem}}"
 | 
			
		||||
                 :makeProps
 | 
			
		||||
                 items="{{$statusIds}}"
 | 
			
		||||
                 items="{{$timelineItemIds}}"
 | 
			
		||||
                 on:scrollToBottom="onScrollToBottom()"
 | 
			
		||||
                 shown="{{$initialized}}"
 | 
			
		||||
                 footerComponent="{{LoadingFooter}}"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +31,7 @@
 | 
			
		|||
         whole thing rather than use a virtual list -->
 | 
			
		||||
    <PseudoVirtualList component="{{StatusVirtualListItem}}"
 | 
			
		||||
                       :makeProps
 | 
			
		||||
                       items="{{$statusIds}}"
 | 
			
		||||
                       items="{{$timelineItemIds}}"
 | 
			
		||||
                       shown="{{$initialized}}"
 | 
			
		||||
                       on:initializedVisibleItems="initialize()"
 | 
			
		||||
                       scrollToItem="{{timelineValue}}"
 | 
			
		||||
| 
						 | 
				
			
			@ -48,13 +59,14 @@
 | 
			
		|||
<script>
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
  import StatusVirtualListItem from './StatusVirtualListItem.html'
 | 
			
		||||
  import NotificationVirtualListItem from './NotificationVirtualListItem.html'
 | 
			
		||||
  import Status from '../status/Status.html'
 | 
			
		||||
  import PseudoVirtualList from '../pseudoVirtualList/PseudoVirtualList.html'
 | 
			
		||||
  import LoadingFooter from './LoadingFooter.html'
 | 
			
		||||
  import VirtualList from '../virtualList/VirtualList.html'
 | 
			
		||||
  import { timelines } from '../../_static/timelines'
 | 
			
		||||
  import { database } from '../../_utils/database/database'
 | 
			
		||||
  import { initializeTimeline, fetchStatusesOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
 | 
			
		||||
  import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
 | 
			
		||||
  import LoadingSpinner from '../LoadingSpinner.html'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
| 
						 | 
				
			
			@ -64,15 +76,20 @@
 | 
			
		|||
    },
 | 
			
		||||
    data: () => ({
 | 
			
		||||
      StatusVirtualListItem,
 | 
			
		||||
      NotificationVirtualListItem,
 | 
			
		||||
      LoadingFooter,
 | 
			
		||||
      Status
 | 
			
		||||
    }),
 | 
			
		||||
    computed: {
 | 
			
		||||
      makeProps: ($currentInstance, timelineType, timelineValue) => async (statusId) => ({
 | 
			
		||||
        timelineType: timelineType,
 | 
			
		||||
        timelineValue: timelineValue,
 | 
			
		||||
        status: await database.getStatus($currentInstance, statusId)
 | 
			
		||||
      }),
 | 
			
		||||
      makeProps: ($currentInstance, timelineType, timelineValue) => async (itemId) => {
 | 
			
		||||
        let res = { timelineType, timelineValue }
 | 
			
		||||
        if (timelineType === 'notifications') {
 | 
			
		||||
          res.notification = await database.getNotification($currentInstance, itemId)
 | 
			
		||||
        } else {
 | 
			
		||||
          res.status = await database.getStatus($currentInstance, itemId)
 | 
			
		||||
        }
 | 
			
		||||
        return res
 | 
			
		||||
      },
 | 
			
		||||
      label: (timeline, $currentInstance, timelineType, timelineValue) => {
 | 
			
		||||
        if (timelines[timeline]) {
 | 
			
		||||
          return `${timelines[timeline].label} timeline for ${$currentInstance}`
 | 
			
		||||
| 
						 | 
				
			
			@ -101,11 +118,12 @@
 | 
			
		|||
    components: {
 | 
			
		||||
      VirtualList,
 | 
			
		||||
      PseudoVirtualList,
 | 
			
		||||
      NotificationVirtualListItem,
 | 
			
		||||
      LoadingSpinner
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      initialize() {
 | 
			
		||||
        if (this.store.get('initialized') || !this.store.get('statusIds') || !this.store.get('statusIds').length) {
 | 
			
		||||
        if (this.store.get('initialized') || !this.store.get('timelineItemIds') || !this.store.get('timelineItemIds').length) {
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
        console.log('timeline initialize()')
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +135,7 @@
 | 
			
		|||
            this.get('timelineType') === 'status') { // for status contexts, we've already fetched the whole thread
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
        fetchStatusesOnScrollToBottom()
 | 
			
		||||
        fetchTimelineItemsOnScrollToBottom()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,8 @@ export function timelineComputations(store) {
 | 
			
		|||
      return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
  store.compute('statusIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.statusIds || [])
 | 
			
		||||
  store.compute('timelineItemIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.timelineItemIds || [])
 | 
			
		||||
  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])
 | 
			
		||||
  store.compute('lastTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds.length && timelineItemIds[timelineItemIds.length - 1])
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,13 +16,18 @@ export const metaCache = {
 | 
			
		|||
  maxSize: 20,
 | 
			
		||||
  caches: {}
 | 
			
		||||
}
 | 
			
		||||
export const notificationsCache = {
 | 
			
		||||
  maxSize: 50,
 | 
			
		||||
  caches: {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (process.browser && process.env.NODE_ENV !== 'production') {
 | 
			
		||||
  window.cacheStats = {
 | 
			
		||||
    statuses: statusesCache,
 | 
			
		||||
    accounts: accountsCache,
 | 
			
		||||
    relationships: relationshipsCache,
 | 
			
		||||
    meta: metaCache
 | 
			
		||||
    meta: metaCache,
 | 
			
		||||
    notifications: notificationsCache
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
export const STATUSES_STORE = 'statuses'
 | 
			
		||||
export const TIMELINE_STORE = 'timelines'
 | 
			
		||||
export const STATUS_TIMELINES_STORE = 'status_timelines'
 | 
			
		||||
export const META_STORE = 'meta'
 | 
			
		||||
export const ACCOUNTS_STORE = 'accounts'
 | 
			
		||||
export const RELATIONSHIPS_STORE = 'relationships'
 | 
			
		||||
export const NOTIFICATIONS_STORE = 'notifications'
 | 
			
		||||
export const NOTIFICATION_TIMELINES_STORE = 'notification_timelines'
 | 
			
		||||
| 
						 | 
				
			
			@ -9,10 +9,11 @@ import {
 | 
			
		|||
 | 
			
		||||
import {
 | 
			
		||||
  META_STORE,
 | 
			
		||||
  TIMELINE_STORE,
 | 
			
		||||
  STATUS_TIMELINES_STORE,
 | 
			
		||||
  STATUSES_STORE,
 | 
			
		||||
  ACCOUNTS_STORE,
 | 
			
		||||
  RELATIONSHIPS_STORE
 | 
			
		||||
  RELATIONSHIPS_STORE,
 | 
			
		||||
  NOTIFICATIONS_STORE, NOTIFICATION_TIMELINES_STORE
 | 
			
		||||
} from './constants'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ import {
 | 
			
		|||
  relationshipsCache,
 | 
			
		||||
  accountsCache,
 | 
			
		||||
  metaCache,
 | 
			
		||||
  notificationsCache,
 | 
			
		||||
  clearCache,
 | 
			
		||||
  getInCache,
 | 
			
		||||
  hasInCache,
 | 
			
		||||
| 
						 | 
				
			
			@ -51,13 +53,29 @@ async function setGenericEntityWithId(store, cache, instanceName, entity) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// timelines/statuses
 | 
			
		||||
// timelines/statuses/notifications
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
function getTimelineVariables(timeline) {
 | 
			
		||||
  if (timeline === 'notifications') {
 | 
			
		||||
    return {
 | 
			
		||||
      stores: [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE],
 | 
			
		||||
      remoteId: 'notificationId',
 | 
			
		||||
      itemsCache: notificationsCache
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    stores: [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE],
 | 
			
		||||
    remoteId: 'statusId',
 | 
			
		||||
    itemsCache: statusesCache
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
 | 
			
		||||
  const db = await getDatabase(instanceName, timeline)
 | 
			
		||||
  return await dbPromise(db, [TIMELINE_STORE, STATUSES_STORE], 'readonly', (stores, callback) => {
 | 
			
		||||
    let [ timelineStore, statusesStore ] = stores
 | 
			
		||||
  let { stores, remoteId } = getTimelineVariables(timeline)
 | 
			
		||||
  const db = await getDatabase(instanceName)
 | 
			
		||||
  return await dbPromise(db, stores, 'readonly', (stores, callback) => {
 | 
			
		||||
    let [ timelineStore, itemsStore ] = stores
 | 
			
		||||
 | 
			
		||||
    let negBigInt = maxId && toReversePaddedBigInt(maxId)
 | 
			
		||||
    let start = negBigInt ? (timeline + '\u0000' + negBigInt) : (timeline + '\u0000')
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +86,7 @@ export async function getTimeline(instanceName, timeline, maxId = null, limit =
 | 
			
		|||
      let timelineResults = e.target.result
 | 
			
		||||
      let res = new Array(timelineResults.length)
 | 
			
		||||
      timelineResults.forEach((timelineResult, i) => {
 | 
			
		||||
        statusesStore.get(timelineResult.statusId).onsuccess = e => {
 | 
			
		||||
        itemsStore.get(timelineResult[remoteId]).onsuccess = e => {
 | 
			
		||||
          res[i] = e.target.result
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
| 
						 | 
				
			
			@ -77,27 +95,28 @@ export async function getTimeline(instanceName, timeline, maxId = null, limit =
 | 
			
		|||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function insertStatuses(instanceName, timeline, statuses) {
 | 
			
		||||
  for (let status of statuses) {
 | 
			
		||||
    setInCache(statusesCache, instanceName, status.id, status)
 | 
			
		||||
    setInCache(accountsCache, instanceName, status.account.id, status.account)
 | 
			
		||||
    if (status.reblog) {
 | 
			
		||||
      setInCache(accountsCache, instanceName, status.reblog.account.id, status.reblog.account)
 | 
			
		||||
export async function insertTimelineItems(instanceName, timeline, timelineItems) {
 | 
			
		||||
  let { stores, remoteId, itemsCache } = getTimelineVariables(timeline)
 | 
			
		||||
  for (let timelineItem of timelineItems) {
 | 
			
		||||
    setInCache(itemsCache, instanceName, timelineItem.id, timelineItem)
 | 
			
		||||
    setInCache(accountsCache, instanceName, timelineItem.account.id, timelineItem.account)
 | 
			
		||||
    if (timelineItem.reblog) {
 | 
			
		||||
      setInCache(accountsCache, instanceName, timelineItem.reblog.account.id, timelineItem.reblog.account)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const db = await getDatabase(instanceName, timeline)
 | 
			
		||||
  await dbPromise(db, [TIMELINE_STORE, STATUSES_STORE, ACCOUNTS_STORE], 'readwrite', (stores) => {
 | 
			
		||||
    let [ timelineStore, statusesStore, accountsStore ] = stores
 | 
			
		||||
    for (let status of statuses) {
 | 
			
		||||
      statusesStore.put(status)
 | 
			
		||||
  const db = await getDatabase(instanceName)
 | 
			
		||||
  await dbPromise(db, stores, 'readwrite', (stores) => {
 | 
			
		||||
    let [ timelineStore, itemsStore, accountsStore ] = stores
 | 
			
		||||
    for (let item of timelineItems) {
 | 
			
		||||
      itemsStore.put(item)
 | 
			
		||||
      // reverse chronological order, prefixed by timeline
 | 
			
		||||
      timelineStore.put({
 | 
			
		||||
        id: (timeline + '\u0000' +  toReversePaddedBigInt(status.id)),
 | 
			
		||||
        statusId: status.id
 | 
			
		||||
        id: (timeline + '\u0000' + toReversePaddedBigInt(item.id)),
 | 
			
		||||
        [remoteId]: item.id
 | 
			
		||||
      })
 | 
			
		||||
      accountsStore.put(status.account)
 | 
			
		||||
      if (status.reblog) {
 | 
			
		||||
        accountsStore.put(status.reblog.account)
 | 
			
		||||
      accountsStore.put(item.account)
 | 
			
		||||
      if (item.reblog) {
 | 
			
		||||
        accountsStore.put(item.reblog.account)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +126,10 @@ export async function getStatus(instanceName, statusId) {
 | 
			
		|||
  return await getGenericEntityWithId(STATUSES_STORE, statusesCache, instanceName, statusId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getNotification(instanceName, notificationId) {
 | 
			
		||||
  return await getGenericEntityWithId(NOTIFICATIONS_STORE, notificationsCache, instanceName, notificationId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// meta
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,16 @@
 | 
			
		|||
const openReqs = {}
 | 
			
		||||
const databaseCache = {}
 | 
			
		||||
 | 
			
		||||
const DB_VERSION = 2
 | 
			
		||||
const DB_VERSION = 1
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  META_STORE,
 | 
			
		||||
  TIMELINE_STORE,
 | 
			
		||||
  STATUS_TIMELINES_STORE,
 | 
			
		||||
  STATUSES_STORE,
 | 
			
		||||
  ACCOUNTS_STORE,
 | 
			
		||||
  RELATIONSHIPS_STORE
 | 
			
		||||
  RELATIONSHIPS_STORE,
 | 
			
		||||
  NOTIFICATIONS_STORE,
 | 
			
		||||
  NOTIFICATION_TIMELINES_STORE
 | 
			
		||||
} from './constants'
 | 
			
		||||
 | 
			
		||||
export function getDatabase(instanceName) {
 | 
			
		||||
| 
						 | 
				
			
			@ -28,16 +30,15 @@ export function getDatabase(instanceName) {
 | 
			
		|||
    }
 | 
			
		||||
    req.onupgradeneeded = (e) => {
 | 
			
		||||
      let db = req.result;
 | 
			
		||||
      if (e.oldVersion < 1) {
 | 
			
		||||
        db.createObjectStore(META_STORE, {keyPath: 'key'})
 | 
			
		||||
        db.createObjectStore(STATUSES_STORE, {keyPath: 'id'})
 | 
			
		||||
        db.createObjectStore(ACCOUNTS_STORE, {keyPath: 'id'})
 | 
			
		||||
        let timelineStore = db.createObjectStore(TIMELINE_STORE, {keyPath: 'id'})
 | 
			
		||||
        timelineStore.createIndex('statusId', 'statusId')
 | 
			
		||||
      }
 | 
			
		||||
      if (e.oldVersion < 2) {
 | 
			
		||||
        db.createObjectStore(RELATIONSHIPS_STORE, {keyPath: 'id'})
 | 
			
		||||
      }
 | 
			
		||||
      db.createObjectStore(META_STORE, {keyPath: 'key'})
 | 
			
		||||
      db.createObjectStore(STATUSES_STORE, {keyPath: 'id'})
 | 
			
		||||
      db.createObjectStore(ACCOUNTS_STORE, {keyPath: 'id'})
 | 
			
		||||
      db.createObjectStore(RELATIONSHIPS_STORE, {keyPath: 'id'})
 | 
			
		||||
      db.createObjectStore(NOTIFICATIONS_STORE, {keyPath: 'id'})
 | 
			
		||||
      db.createObjectStore(STATUS_TIMELINES_STORE, {keyPath: 'id'})
 | 
			
		||||
          .createIndex('statusId', 'statusId')
 | 
			
		||||
      db.createObjectStore(NOTIFICATION_TIMELINES_STORE, {keyPath: 'id'})
 | 
			
		||||
          .createIndex('notificationId', 'notificationId')
 | 
			
		||||
    }
 | 
			
		||||
    req.onsuccess = () => resolve(req.result)
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,8 @@ function getTimelineUrlPath(timeline) {
 | 
			
		|||
      return 'timelines/public'
 | 
			
		||||
    case 'home':
 | 
			
		||||
      return 'timelines/home'
 | 
			
		||||
    case 'notifications':
 | 
			
		||||
      return 'notifications'
 | 
			
		||||
  }
 | 
			
		||||
  if (timeline.startsWith('tag/')) {
 | 
			
		||||
    return 'timelines/tag'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,10 @@
 | 
			
		|||
	<title>Pinafore – Notifications</title>
 | 
			
		||||
</:Head>
 | 
			
		||||
 | 
			
		||||
<Layout page='notifications' virtual="true" virtualRealm="federated">
 | 
			
		||||
<Layout page='notifications' virtual="true" virtualRealm="notifications">
 | 
			
		||||
  {{#if $isUserLoggedIn}}
 | 
			
		||||
  <LazyTimeline timeline='notifications' />
 | 
			
		||||
  {{else}}
 | 
			
		||||
  <HiddenFromSSR>
 | 
			
		||||
    <FreeTextLayout>
 | 
			
		||||
      <h1>Notifications</h1>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,18 +13,23 @@
 | 
			
		|||
      <p>Your notifications will appear here when logged in.</p>
 | 
			
		||||
    </FreeTextLayout>
 | 
			
		||||
  </HiddenFromSSR>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
</Layout>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
  import Layout from './_components/Layout.html';
 | 
			
		||||
  import Layout from './_components/Layout.html'
 | 
			
		||||
  import LazyTimeline from './_components/timeline/LazyTimeline.html'
 | 
			
		||||
  import FreeTextLayout from './_components/FreeTextLayout.html'
 | 
			
		||||
  import { store } from './_store/store.js'
 | 
			
		||||
  import HiddenFromSSR from './_components/HiddenFromSSR'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    components: {
 | 
			
		||||
      Layout,
 | 
			
		||||
      LazyTimeline,
 | 
			
		||||
      FreeTextLayout,
 | 
			
		||||
      HiddenFromSSR
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue