refactor database some more
This commit is contained in:
		
							parent
							
								
									08b3754746
								
							
						
					
					
						commit
						537a112adb
					
				
					 7 changed files with 234 additions and 267 deletions
				
			
		
							
								
								
									
										19
									
								
								routes/_database/accountsAndRelationships.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								routes/_database/accountsAndRelationships.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					import { ACCOUNTS_STORE, RELATIONSHIPS_STORE } from './constants'
 | 
				
			||||||
 | 
					import { accountsCache, relationshipsCache } from './cache'
 | 
				
			||||||
 | 
					import { getGenericEntityWithId, setGenericEntityWithId } from './helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getAccount(instanceName, accountId) {
 | 
				
			||||||
 | 
					  return await getGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, accountId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setAccount(instanceName, account) {
 | 
				
			||||||
 | 
					  return await setGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, account)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getRelationship(instanceName, accountId) {
 | 
				
			||||||
 | 
					  return await getGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, accountId)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setRelationship(instanceName, relationship) {
 | 
				
			||||||
 | 
					  return await setGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, relationship)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								routes/_database/clear.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								routes/_database/clear.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					import { accountsCache, clearCache, metaCache, statusesCache } from './cache'
 | 
				
			||||||
 | 
					import { deleteDatabase } from './databaseLifecycle'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function clearDatabaseForInstance(instanceName) {
 | 
				
			||||||
 | 
					  clearCache(statusesCache, instanceName)
 | 
				
			||||||
 | 
					  clearCache(accountsCache, instanceName)
 | 
				
			||||||
 | 
					  clearCache(metaCache, instanceName)
 | 
				
			||||||
 | 
					  await deleteDatabase(instanceName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,3 @@
 | 
				
			||||||
/*
 | 
					import * as database from './databaseCore'
 | 
				
			||||||
import worker from 'workerize-loader!./databaseCore'
 | 
					 | 
				
			||||||
export const database = process.browser && worker()
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as dbCore from './databaseCore'
 | 
					export { database }
 | 
				
			||||||
 | 
					 | 
				
			||||||
export { dbCore as database }
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,260 +1,4 @@
 | 
				
			||||||
import {
 | 
					export * from './accountsAndRelationships'
 | 
				
			||||||
  toReversePaddedBigInt
 | 
					export * from './clear'
 | 
				
			||||||
} from './utils'
 | 
					export * from './meta'
 | 
				
			||||||
import {
 | 
					export * from './timelines'
 | 
				
			||||||
  getDatabase,
 | 
					 | 
				
			||||||
  dbPromise,
 | 
					 | 
				
			||||||
  deleteDatabase,
 | 
					 | 
				
			||||||
} from './databaseLifecycle'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  META_STORE,
 | 
					 | 
				
			||||||
  STATUS_TIMELINES_STORE,
 | 
					 | 
				
			||||||
  STATUSES_STORE,
 | 
					 | 
				
			||||||
  ACCOUNTS_STORE,
 | 
					 | 
				
			||||||
  RELATIONSHIPS_STORE,
 | 
					 | 
				
			||||||
  NOTIFICATIONS_STORE,
 | 
					 | 
				
			||||||
  NOTIFICATION_TIMELINES_STORE
 | 
					 | 
				
			||||||
} from './constants'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  statusesCache,
 | 
					 | 
				
			||||||
  relationshipsCache,
 | 
					 | 
				
			||||||
  accountsCache,
 | 
					 | 
				
			||||||
  metaCache,
 | 
					 | 
				
			||||||
  notificationsCache,
 | 
					 | 
				
			||||||
  clearCache,
 | 
					 | 
				
			||||||
  getInCache,
 | 
					 | 
				
			||||||
  hasInCache,
 | 
					 | 
				
			||||||
  setInCache
 | 
					 | 
				
			||||||
} from './cache'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// helpers
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getGenericEntityWithId(store, cache, instanceName, id) {
 | 
					 | 
				
			||||||
  if (hasInCache(cache, instanceName, id)) {
 | 
					 | 
				
			||||||
    return getInCache(cache, instanceName, id)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  let result = await dbPromise(db, store, 'readonly', (store, callback) => {
 | 
					 | 
				
			||||||
    store.get(id).onsuccess = (e) => callback(e.target.result)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  setInCache(cache, instanceName, id, result)
 | 
					 | 
				
			||||||
  return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function setGenericEntityWithId(store, cache, instanceName, entity) {
 | 
					 | 
				
			||||||
  setInCache(cache, instanceName, entity.id, entity)
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  return await dbPromise(db, store, 'readwrite', (store) => {
 | 
					 | 
				
			||||||
    store.put(entity)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// timelines/statuses/notifications
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function createKeyRange(timeline, maxId) {
 | 
					 | 
				
			||||||
  let negBigInt = maxId && toReversePaddedBigInt(maxId)
 | 
					 | 
				
			||||||
  let start = negBigInt ? (timeline + '\u0000' + negBigInt) : (timeline + '\u0000')
 | 
					 | 
				
			||||||
  let end = timeline + '\u0000\uffff'
 | 
					 | 
				
			||||||
  return IDBKeyRange.bound(start, end, false, false)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getNotificationTimeline(instanceName, timeline, maxId, limit) {
 | 
					 | 
				
			||||||
  let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE]
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
 | 
					 | 
				
			||||||
    let [ timelineStore, notificationsStore ] = stores
 | 
					 | 
				
			||||||
    let keyRange = createKeyRange(timeline, maxId)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    timelineStore.getAll(keyRange, limit).onsuccess = e => {
 | 
					 | 
				
			||||||
      let timelineResults = e.target.result
 | 
					 | 
				
			||||||
      let res = new Array(timelineResults.length)
 | 
					 | 
				
			||||||
      timelineResults.forEach((timelineResult, i) => {
 | 
					 | 
				
			||||||
        notificationsStore.get(timelineResult.notificationId).onsuccess = e => {
 | 
					 | 
				
			||||||
          res[i] = e.target.result
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      callback(res)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getStatusTimeline(instanceName, timeline, maxId, limit) {
 | 
					 | 
				
			||||||
  let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE]
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
 | 
					 | 
				
			||||||
    let [ timelineStore, statusesStore ] = stores
 | 
					 | 
				
			||||||
    let keyRange = createKeyRange(timeline, maxId)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    timelineStore.getAll(keyRange, limit).onsuccess = e => {
 | 
					 | 
				
			||||||
      let timelineResults = e.target.result
 | 
					 | 
				
			||||||
      let res = new Array(timelineResults.length)
 | 
					 | 
				
			||||||
      timelineResults.forEach((timelineResult, i) => {
 | 
					 | 
				
			||||||
        statusesStore.get(timelineResult.statusId).onsuccess = e => {
 | 
					 | 
				
			||||||
          res[i] = e.target.result
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      callback(res)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
 | 
					 | 
				
			||||||
  return timeline === 'notifications' ?
 | 
					 | 
				
			||||||
    await getNotificationTimeline(instanceName, timeline, maxId, limit) :
 | 
					 | 
				
			||||||
    await getStatusTimeline(instanceName, timeline, maxId, limit)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function createTimelineId(timeline, id) {
 | 
					 | 
				
			||||||
  // reverse chronological order, prefixed by timeline
 | 
					 | 
				
			||||||
  return timeline + '\u0000' + toReversePaddedBigInt(id)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function insertTimelineNotifications(instanceName, timeline, notifications) {
 | 
					 | 
				
			||||||
  let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE]
 | 
					 | 
				
			||||||
  for (let notification of notifications) {
 | 
					 | 
				
			||||||
    setInCache(notificationsCache, instanceName, notification.id, notification)
 | 
					 | 
				
			||||||
    setInCache(accountsCache, instanceName, notification.account.id, notification.account)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  await dbPromise(db, storeNames, 'readwrite', (stores) => {
 | 
					 | 
				
			||||||
    let [ timelineStore, notificationsStore, accountsStore ] = stores
 | 
					 | 
				
			||||||
    for (let notification of notifications) {
 | 
					 | 
				
			||||||
      notificationsStore.put(notification)
 | 
					 | 
				
			||||||
      timelineStore.put({
 | 
					 | 
				
			||||||
        id: createTimelineId(timeline, notification.id),
 | 
					 | 
				
			||||||
        notificationId: notification.id
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      accountsStore.put(notification.account)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function insertTimelineStatuses(instanceName, timeline, statuses) {
 | 
					 | 
				
			||||||
  let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
 | 
					 | 
				
			||||||
  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)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  await dbPromise(db, storeNames, 'readwrite', (stores) => {
 | 
					 | 
				
			||||||
    let [ timelineStore, statusesStore, accountsStore ] = stores
 | 
					 | 
				
			||||||
    for (let status of statuses) {
 | 
					 | 
				
			||||||
      statusesStore.put(status)
 | 
					 | 
				
			||||||
      timelineStore.put({
 | 
					 | 
				
			||||||
        id: createTimelineId(timeline, status.id),
 | 
					 | 
				
			||||||
        statusId: status.id
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      accountsStore.put(status.account)
 | 
					 | 
				
			||||||
      if (status.reblog) {
 | 
					 | 
				
			||||||
        accountsStore.put(status.reblog.account)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function insertTimelineItems(instanceName, timeline, timelineItems) {
 | 
					 | 
				
			||||||
  return timeline === 'notifications' ?
 | 
					 | 
				
			||||||
    await insertTimelineNotifications(instanceName, timeline, timelineItems) :
 | 
					 | 
				
			||||||
    await insertTimelineStatuses(instanceName, timeline, timelineItems)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getMetaProperty(instanceName, key) {
 | 
					 | 
				
			||||||
  if (hasInCache(metaCache, instanceName, key)) {
 | 
					 | 
				
			||||||
    return getInCache(metaCache, instanceName, key)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  let result = await dbPromise(db, META_STORE, 'readonly', (store, callback) => {
 | 
					 | 
				
			||||||
    store.get(key).onsuccess = (e) => {
 | 
					 | 
				
			||||||
      callback(e.target.result && e.target.result.value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  setInCache(metaCache, instanceName, key, result)
 | 
					 | 
				
			||||||
  return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function setMetaProperty(instanceName, key, value) {
 | 
					 | 
				
			||||||
  setInCache(metaCache, instanceName, key, value)
 | 
					 | 
				
			||||||
  const db = await getDatabase(instanceName)
 | 
					 | 
				
			||||||
  return await dbPromise(db, META_STORE, 'readwrite', (store) => {
 | 
					 | 
				
			||||||
    store.put({
 | 
					 | 
				
			||||||
      key: key,
 | 
					 | 
				
			||||||
      value: value
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getInstanceVerifyCredentials(instanceName) {
 | 
					 | 
				
			||||||
  return await getMetaProperty(instanceName, 'verifyCredentials')
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function setInstanceVerifyCredentials(instanceName, value) {
 | 
					 | 
				
			||||||
  return await setMetaProperty(instanceName, 'verifyCredentials', value)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getInstanceInfo(instanceName) {
 | 
					 | 
				
			||||||
  return await getMetaProperty(instanceName, 'instance')
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function setInstanceInfo(instanceName, value) {
 | 
					 | 
				
			||||||
  return await setMetaProperty(instanceName, 'instance', value)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getLists(instanceName) {
 | 
					 | 
				
			||||||
  return await getMetaProperty(instanceName, 'lists')
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function setLists(instanceName, value) {
 | 
					 | 
				
			||||||
  return await setMetaProperty(instanceName, 'lists', value)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// accounts/relationships
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getAccount(instanceName, accountId) {
 | 
					 | 
				
			||||||
  return await getGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, accountId)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function setAccount(instanceName, account) {
 | 
					 | 
				
			||||||
  return await setGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, account)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function getRelationship(instanceName, accountId) {
 | 
					 | 
				
			||||||
  return await getGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, accountId)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function setRelationship(instanceName, relationship) {
 | 
					 | 
				
			||||||
  return await setGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, relationship)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// lifecycle
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function clearDatabaseForInstance(instanceName) {
 | 
					 | 
				
			||||||
  clearCache(statusesCache, instanceName)
 | 
					 | 
				
			||||||
  clearCache(accountsCache, instanceName)
 | 
					 | 
				
			||||||
  clearCache(metaCache, instanceName)
 | 
					 | 
				
			||||||
  await deleteDatabase(instanceName)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										22
									
								
								routes/_database/helpers.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								routes/_database/helpers.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					import { dbPromise, getDatabase } from './databaseLifecycle'
 | 
				
			||||||
 | 
					import { getInCache, hasInCache, setInCache } from './cache'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getGenericEntityWithId(store, cache, instanceName, id) {
 | 
				
			||||||
 | 
					  if (hasInCache(cache, instanceName, id)) {
 | 
				
			||||||
 | 
					    return getInCache(cache, instanceName, id)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  let result = await dbPromise(db, store, 'readonly', (store, callback) => {
 | 
				
			||||||
 | 
					    store.get(id).onsuccess = (e) => callback(e.target.result)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  setInCache(cache, instanceName, id, result)
 | 
				
			||||||
 | 
					  return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setGenericEntityWithId(store, cache, instanceName, entity) {
 | 
				
			||||||
 | 
					  setInCache(cache, instanceName, entity.id, entity)
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  return await dbPromise(db, store, 'readwrite', (store) => {
 | 
				
			||||||
 | 
					    store.put(entity)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										52
									
								
								routes/_database/meta.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								routes/_database/meta.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					import { dbPromise, getDatabase } from './databaseLifecycle'
 | 
				
			||||||
 | 
					import { META_STORE } from './constants'
 | 
				
			||||||
 | 
					import { metaCache, hasInCache, getInCache, setInCache } from './cache'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function getMetaProperty(instanceName, key) {
 | 
				
			||||||
 | 
					  if (hasInCache(metaCache, instanceName, key)) {
 | 
				
			||||||
 | 
					    return getInCache(metaCache, instanceName, key)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  let result = await dbPromise(db, META_STORE, 'readonly', (store, callback) => {
 | 
				
			||||||
 | 
					    store.get(key).onsuccess = (e) => {
 | 
				
			||||||
 | 
					      callback(e.target.result && e.target.result.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  setInCache(metaCache, instanceName, key, result)
 | 
				
			||||||
 | 
					  return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function setMetaProperty(instanceName, key, value) {
 | 
				
			||||||
 | 
					  setInCache(metaCache, instanceName, key, value)
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  return await dbPromise(db, META_STORE, 'readwrite', (store) => {
 | 
				
			||||||
 | 
					    store.put({
 | 
				
			||||||
 | 
					      key: key,
 | 
				
			||||||
 | 
					      value: value
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getInstanceVerifyCredentials(instanceName) {
 | 
				
			||||||
 | 
					  return await getMetaProperty(instanceName, 'verifyCredentials')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setInstanceVerifyCredentials(instanceName, value) {
 | 
				
			||||||
 | 
					  return await setMetaProperty(instanceName, 'verifyCredentials', value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getInstanceInfo(instanceName) {
 | 
				
			||||||
 | 
					  return await getMetaProperty(instanceName, 'instance')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setInstanceInfo(instanceName, value) {
 | 
				
			||||||
 | 
					  return await setMetaProperty(instanceName, 'instance', value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getLists(instanceName) {
 | 
				
			||||||
 | 
					  return await getMetaProperty(instanceName, 'lists')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setLists(instanceName, value) {
 | 
				
			||||||
 | 
					  return await setMetaProperty(instanceName, 'lists', value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										126
									
								
								routes/_database/timelines.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								routes/_database/timelines.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,126 @@
 | 
				
			||||||
 | 
					import { toReversePaddedBigInt } from './utils'
 | 
				
			||||||
 | 
					import { dbPromise, getDatabase } from './databaseLifecycle'
 | 
				
			||||||
 | 
					import { accountsCache, notificationsCache, setInCache, statusesCache } from './cache'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ACCOUNTS_STORE, NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, STATUS_TIMELINES_STORE,
 | 
				
			||||||
 | 
					  STATUSES_STORE
 | 
				
			||||||
 | 
					} from './constants'
 | 
				
			||||||
 | 
					import { getGenericEntityWithId } from './helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createKeyRange(timeline, maxId) {
 | 
				
			||||||
 | 
					  let negBigInt = maxId && toReversePaddedBigInt(maxId)
 | 
				
			||||||
 | 
					  let start = negBigInt ? (timeline + '\u0000' + negBigInt) : (timeline + '\u0000')
 | 
				
			||||||
 | 
					  let end = timeline + '\u0000\uffff'
 | 
				
			||||||
 | 
					  return IDBKeyRange.bound(start, end, false, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function getNotificationTimeline(instanceName, timeline, maxId, limit) {
 | 
				
			||||||
 | 
					  let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE]
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
 | 
				
			||||||
 | 
					    let [ timelineStore, notificationsStore ] = stores
 | 
				
			||||||
 | 
					    let keyRange = createKeyRange(timeline, maxId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timelineStore.getAll(keyRange, limit).onsuccess = e => {
 | 
				
			||||||
 | 
					      let timelineResults = e.target.result
 | 
				
			||||||
 | 
					      let res = new Array(timelineResults.length)
 | 
				
			||||||
 | 
					      timelineResults.forEach((timelineResult, i) => {
 | 
				
			||||||
 | 
					        notificationsStore.get(timelineResult.notificationId).onsuccess = e => {
 | 
				
			||||||
 | 
					          res[i] = e.target.result
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      callback(res)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function getStatusTimeline(instanceName, timeline, maxId, limit) {
 | 
				
			||||||
 | 
					  let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE]
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
 | 
				
			||||||
 | 
					    let [ timelineStore, statusesStore ] = stores
 | 
				
			||||||
 | 
					    let keyRange = createKeyRange(timeline, maxId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    timelineStore.getAll(keyRange, limit).onsuccess = e => {
 | 
				
			||||||
 | 
					      let timelineResults = e.target.result
 | 
				
			||||||
 | 
					      let res = new Array(timelineResults.length)
 | 
				
			||||||
 | 
					      timelineResults.forEach((timelineResult, i) => {
 | 
				
			||||||
 | 
					        statusesStore.get(timelineResult.statusId).onsuccess = e => {
 | 
				
			||||||
 | 
					          res[i] = e.target.result
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      callback(res)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
 | 
				
			||||||
 | 
					  return timeline === 'notifications' ?
 | 
				
			||||||
 | 
					    await getNotificationTimeline(instanceName, timeline, maxId, limit) :
 | 
				
			||||||
 | 
					    await getStatusTimeline(instanceName, timeline, maxId, limit)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createTimelineId(timeline, id) {
 | 
				
			||||||
 | 
					  // reverse chronological order, prefixed by timeline
 | 
				
			||||||
 | 
					  return timeline + '\u0000' + toReversePaddedBigInt(id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function insertTimelineNotifications(instanceName, timeline, notifications) {
 | 
				
			||||||
 | 
					  let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE]
 | 
				
			||||||
 | 
					  for (let notification of notifications) {
 | 
				
			||||||
 | 
					    setInCache(notificationsCache, instanceName, notification.id, notification)
 | 
				
			||||||
 | 
					    setInCache(accountsCache, instanceName, notification.account.id, notification.account)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  await dbPromise(db, storeNames, 'readwrite', (stores) => {
 | 
				
			||||||
 | 
					    let [ timelineStore, notificationsStore, accountsStore ] = stores
 | 
				
			||||||
 | 
					    for (let notification of notifications) {
 | 
				
			||||||
 | 
					      notificationsStore.put(notification)
 | 
				
			||||||
 | 
					      timelineStore.put({
 | 
				
			||||||
 | 
					        id: createTimelineId(timeline, notification.id),
 | 
				
			||||||
 | 
					        notificationId: notification.id
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      accountsStore.put(notification.account)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function insertTimelineStatuses(instanceName, timeline, statuses) {
 | 
				
			||||||
 | 
					  let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
 | 
				
			||||||
 | 
					  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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const db = await getDatabase(instanceName)
 | 
				
			||||||
 | 
					  await dbPromise(db, storeNames, 'readwrite', (stores) => {
 | 
				
			||||||
 | 
					    let [ timelineStore, statusesStore, accountsStore ] = stores
 | 
				
			||||||
 | 
					    for (let status of statuses) {
 | 
				
			||||||
 | 
					      statusesStore.put(status)
 | 
				
			||||||
 | 
					      timelineStore.put({
 | 
				
			||||||
 | 
					        id: createTimelineId(timeline, status.id),
 | 
				
			||||||
 | 
					        statusId: status.id
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      accountsStore.put(status.account)
 | 
				
			||||||
 | 
					      if (status.reblog) {
 | 
				
			||||||
 | 
					        accountsStore.put(status.reblog.account)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function insertTimelineItems(instanceName, timeline, timelineItems) {
 | 
				
			||||||
 | 
					  return timeline === 'notifications' ?
 | 
				
			||||||
 | 
					    await insertTimelineNotifications(instanceName, timeline, timelineItems) :
 | 
				
			||||||
 | 
					    await insertTimelineStatuses(instanceName, timeline, timelineItems)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue