forked from cybrespace/pinafore
127 lines
4.8 KiB
JavaScript
127 lines
4.8 KiB
JavaScript
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 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 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'
|
|
? getNotificationTimeline(instanceName, timeline, maxId, limit)
|
|
: 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'
|
|
? insertTimelineNotifications(instanceName, timeline, timelineItems)
|
|
: insertTimelineStatuses(instanceName, timeline, timelineItems)
|
|
}
|
|
|
|
export async function getStatus (instanceName, statusId) {
|
|
return getGenericEntityWithId(STATUSES_STORE, statusesCache, instanceName, statusId)
|
|
}
|
|
|
|
export async function getNotification (instanceName, notificationId) {
|
|
return getGenericEntityWithId(NOTIFICATIONS_STORE, notificationsCache, instanceName, notificationId)
|
|
}
|