Refactor database for better code-splitting (#144)
This commit is contained in:
parent
8fb00a961c
commit
b231466fff
|
@ -1,11 +1,16 @@
|
||||||
import { getAccount, getRelationship } from '../_api/user'
|
import { getAccount, getRelationship } from '../_api/user'
|
||||||
import { database } from '../_database/database'
|
import {
|
||||||
|
getAccount as getAccountFromDatabase,
|
||||||
|
setAccount as setAccountInDatabase,
|
||||||
|
getRelationship as getRelationshipFromDatabase,
|
||||||
|
setRelationship as setRelationshipInDatabase
|
||||||
|
} from '../_database/accountsAndRelationships'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
|
|
||||||
async function updateAccount (accountId, instanceName, accessToken) {
|
async function updateAccount (accountId, instanceName, accessToken) {
|
||||||
let localPromise = database.getAccount(instanceName, accountId)
|
let localPromise = getAccountFromDatabase(instanceName, accountId)
|
||||||
let remotePromise = getAccount(instanceName, accessToken, accountId).then(account => {
|
let remotePromise = getAccount(instanceName, accessToken, accountId).then(account => {
|
||||||
database.setAccount(instanceName, account)
|
/* no await */ setAccountInDatabase(instanceName, account)
|
||||||
return account
|
return account
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -22,9 +27,9 @@ async function updateAccount (accountId, instanceName, accessToken) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateRelationship (accountId, instanceName, accessToken) {
|
async function updateRelationship (accountId, instanceName, accessToken) {
|
||||||
let localPromise = database.getRelationship(instanceName, accountId)
|
let localPromise = getRelationshipFromDatabase(instanceName, accountId)
|
||||||
let remotePromise = getRelationship(instanceName, accessToken, accountId).then(relationship => {
|
let remotePromise = getRelationship(instanceName, accessToken, accountId).then(relationship => {
|
||||||
database.setRelationship(instanceName, relationship)
|
/* no await */ setRelationshipInDatabase(instanceName, relationship)
|
||||||
return relationship
|
return relationship
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { getAccessTokenFromAuthCode, registerApplication, generateAuthLink } fro
|
||||||
import { getInstanceInfo } from '../_api/instance'
|
import { getInstanceInfo } from '../_api/instance'
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
import { switchToTheme } from '../_utils/themeEngine'
|
import { switchToTheme } from '../_utils/themeEngine'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { updateVerifyCredentialsForInstance } from './instances'
|
import { updateVerifyCredentialsForInstance } from './instances'
|
||||||
import { updateCustomEmojiForInstance } from './emoji'
|
import { updateCustomEmojiForInstance } from './emoji'
|
||||||
|
import { setInstanceInfo as setInstanceInfoInDatabase } from '../_database/meta'
|
||||||
|
|
||||||
const REDIRECT_URI = (typeof location !== 'undefined'
|
const REDIRECT_URI = (typeof location !== 'undefined'
|
||||||
? location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
? location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
||||||
|
@ -20,7 +20,7 @@ async function redirectToOauth () {
|
||||||
}
|
}
|
||||||
let registrationPromise = registerApplication(instanceName, REDIRECT_URI)
|
let registrationPromise = registerApplication(instanceName, REDIRECT_URI)
|
||||||
let instanceInfo = await getInstanceInfo(instanceName)
|
let instanceInfo = await getInstanceInfo(instanceName)
|
||||||
await database.setInstanceInfo(instanceName, instanceInfo) // cache for later
|
await setInstanceInfoInDatabase(instanceName, instanceInfo) // cache for later
|
||||||
let instanceData = await registrationPromise
|
let instanceData = await registrationPromise
|
||||||
store.set({
|
store.set({
|
||||||
currentRegisteredInstanceName: instanceName,
|
currentRegisteredInstanceName: instanceName,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import throttle from 'lodash-es/throttle'
|
import throttle from 'lodash-es/throttle'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { mark, stop } from '../_utils/marks'
|
import { mark, stop } from '../_utils/marks'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
|
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
|
||||||
|
@ -7,6 +6,9 @@ import uniqBy from 'lodash-es/uniqBy'
|
||||||
import uniq from 'lodash-es/uniq'
|
import uniq from 'lodash-es/uniq'
|
||||||
import isEqual from 'lodash-es/isEqual'
|
import isEqual from 'lodash-es/isEqual'
|
||||||
import { isMobile } from '../_utils/isMobile'
|
import { isMobile } from '../_utils/isMobile'
|
||||||
|
import {
|
||||||
|
insertTimelineItems as insertTimelineItemsInDatabase
|
||||||
|
} from '../_database/timelines/insertion'
|
||||||
|
|
||||||
const STREAMING_THROTTLE_DELAY = 3000
|
const STREAMING_THROTTLE_DELAY = 3000
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ async function insertUpdatesIntoTimeline (instanceName, timelineName, updates) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await database.insertTimelineItems(instanceName, timelineName, updates)
|
await insertTimelineItemsInDatabase(instanceName, timelineName, updates)
|
||||||
|
|
||||||
let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || []
|
let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || []
|
||||||
let newItemIdsToAdd = uniq([].concat(itemIdsToAdd).concat(updates.map(_ => _.id)))
|
let newItemIdsToAdd = uniq([].concat(itemIdsToAdd).concat(updates.map(_ => _.id)))
|
||||||
|
|
|
@ -2,13 +2,13 @@ import { store } from '../_store/store'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { postStatus as postStatusToServer } from '../_api/statuses'
|
import { postStatus as postStatusToServer } from '../_api/statuses'
|
||||||
import { addStatusOrNotification } from './addStatusOrNotification'
|
import { addStatusOrNotification } from './addStatusOrNotification'
|
||||||
import { database } from '../_database/database'
|
import { getStatus as getStatusFromDatabase } from '../_database/timelines/getStatusOrNotification'
|
||||||
import { emit } from '../_utils/eventBus'
|
import { emit } from '../_utils/eventBus'
|
||||||
import { putMediaDescription } from '../_api/media'
|
import { putMediaDescription } from '../_api/media'
|
||||||
|
|
||||||
export async function insertHandleForReply (statusId) {
|
export async function insertHandleForReply (statusId) {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
let status = await database.getStatus(instanceName, statusId)
|
let status = await getStatusFromDatabase(instanceName, statusId)
|
||||||
let verifyCredentials = store.get('currentVerifyCredentials')
|
let verifyCredentials = store.get('currentVerifyCredentials')
|
||||||
let originalStatus = status.reblog || status
|
let originalStatus = status.reblog || status
|
||||||
let accounts = [originalStatus.account].concat(originalStatus.mentions || [])
|
let accounts = [originalStatus.account].concat(originalStatus.mentions || [])
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { getIdsThatRebloggedThisStatus, getNotificationIdsForStatuses } from './statuses'
|
import { getIdsThatRebloggedThisStatus, getNotificationIdsForStatuses } from './statuses'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
|
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import forEach from 'lodash-es/forEach'
|
import forEach from 'lodash-es/forEach'
|
||||||
import isEqual from 'lodash-es/isEqual'
|
import isEqual from 'lodash-es/isEqual'
|
||||||
|
import {
|
||||||
|
deleteStatusesAndNotifications as deleteStatusesAndNotificationsFromDatabase
|
||||||
|
} from '../_database/timelines/deletion'
|
||||||
|
|
||||||
function filterItemIdsFromTimelines (instanceName, timelineFilter, idFilter) {
|
function filterItemIdsFromTimelines (instanceName, timelineFilter, idFilter) {
|
||||||
let keys = ['timelineItemIds', 'itemIdsToAdd']
|
let keys = ['timelineItemIds', 'itemIdsToAdd']
|
||||||
|
@ -43,7 +45,7 @@ function deleteNotificationIdsFromStore (instanceName, idsToDelete) {
|
||||||
async function deleteStatusesAndNotifications (instanceName, statusIdsToDelete, notificationIdsToDelete) {
|
async function deleteStatusesAndNotifications (instanceName, statusIdsToDelete, notificationIdsToDelete) {
|
||||||
deleteStatusIdsFromStore(instanceName, statusIdsToDelete)
|
deleteStatusIdsFromStore(instanceName, statusIdsToDelete)
|
||||||
deleteNotificationIdsFromStore(instanceName, notificationIdsToDelete)
|
deleteNotificationIdsFromStore(instanceName, notificationIdsToDelete)
|
||||||
await database.deleteStatusesAndNotifications(instanceName, statusIdsToDelete, notificationIdsToDelete)
|
await deleteStatusesAndNotificationsFromDatabase(instanceName, statusIdsToDelete, notificationIdsToDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doDeleteStatus (instanceName, statusId) {
|
async function doDeleteStatus (instanceName, statusId) {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
import { database } from '../_database/database'
|
import {
|
||||||
|
getCustomEmoji as getCustomEmojiFromDatabase,
|
||||||
|
setCustomEmoji as setCustomEmojiInDatabase
|
||||||
|
} from '../_database/meta'
|
||||||
import { getCustomEmoji } from '../_api/emoji'
|
import { getCustomEmoji } from '../_api/emoji'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { substring } from 'stringz'
|
import { substring } from 'stringz'
|
||||||
|
@ -7,8 +10,8 @@ import { substring } from 'stringz'
|
||||||
export async function updateCustomEmojiForInstance (instanceName) {
|
export async function updateCustomEmojiForInstance (instanceName) {
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
() => getCustomEmoji(instanceName),
|
() => getCustomEmoji(instanceName),
|
||||||
() => database.getCustomEmoji(instanceName),
|
() => getCustomEmojiFromDatabase(instanceName),
|
||||||
emoji => database.setCustomEmoji(instanceName, emoji),
|
emoji => setCustomEmojiInDatabase(instanceName, emoji),
|
||||||
emoji => {
|
emoji => {
|
||||||
let customEmoji = store.get('customEmoji')
|
let customEmoji = store.get('customEmoji')
|
||||||
customEmoji[instanceName] = emoji
|
customEmoji[instanceName] = emoji
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { favoriteStatus, unfavoriteStatus } from '../_api/favorite'
|
import { favoriteStatus, unfavoriteStatus } from '../_api/favorite'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
|
import {
|
||||||
|
setStatusFavorited as setStatusFavoritedInDatabase
|
||||||
|
} from '../_database/timelines/updateStatus'
|
||||||
|
|
||||||
export async function setFavorited (statusId, favorited) {
|
export async function setFavorited (statusId, favorited) {
|
||||||
if (!store.get('online')) {
|
if (!store.get('online')) {
|
||||||
|
@ -16,7 +18,7 @@ export async function setFavorited (statusId, favorited) {
|
||||||
store.setStatusFavorited(instanceName, statusId, favorited) // optimistic update
|
store.setStatusFavorited(instanceName, statusId, favorited) // optimistic update
|
||||||
try {
|
try {
|
||||||
await networkPromise
|
await networkPromise
|
||||||
await database.setStatusFavorited(instanceName, statusId, favorited)
|
await setStatusFavoritedInDatabase(instanceName, statusId, favorited)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say(`Failed to ${favorited ? 'favorite' : 'unfavorite'}. ` + (e.message || ''))
|
toast.say(`Failed to ${favorited ? 'favorite' : 'unfavorite'}. ` + (e.message || ''))
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { followAccount, unfollowAccount } from '../_api/follow'
|
import { followAccount, unfollowAccount } from '../_api/follow'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { updateProfileAndRelationship } from './accounts'
|
import { updateProfileAndRelationship } from './accounts'
|
||||||
|
import {
|
||||||
|
getRelationship as getRelationshipFromDatabase
|
||||||
|
} from '../_database/accountsAndRelationships'
|
||||||
|
|
||||||
export async function setAccountFollowed (accountId, follow, toastOnSuccess) {
|
export async function setAccountFollowed (accountId, follow, toastOnSuccess) {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
|
@ -15,7 +17,7 @@ export async function setAccountFollowed (accountId, follow, toastOnSuccess) {
|
||||||
account = await unfollowAccount(instanceName, accessToken, accountId)
|
account = await unfollowAccount(instanceName, accessToken, accountId)
|
||||||
}
|
}
|
||||||
await updateProfileAndRelationship(accountId)
|
await updateProfileAndRelationship(accountId)
|
||||||
let relationship = await database.getRelationship(instanceName, accountId)
|
let relationship = await getRelationshipFromDatabase(instanceName, accountId)
|
||||||
if (toastOnSuccess) {
|
if (toastOnSuccess) {
|
||||||
if (follow) {
|
if (follow) {
|
||||||
if (account.locked && relationship.requested) {
|
if (account.locked && relationship.requested) {
|
||||||
|
|
|
@ -2,10 +2,16 @@ import { getVerifyCredentials } from '../_api/user'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { switchToTheme } from '../_utils/themeEngine'
|
import { switchToTheme } from '../_utils/themeEngine'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
import { getInstanceInfo } from '../_api/instance'
|
import { getInstanceInfo } from '../_api/instance'
|
||||||
|
import { clearDatabaseForInstance } from '../_database/clear'
|
||||||
|
import {
|
||||||
|
getInstanceVerifyCredentials as getInstanceVerifyCredentialsFromDatabase,
|
||||||
|
setInstanceVerifyCredentials as setInstanceVerifyCredentialsInDatabase,
|
||||||
|
getInstanceInfo as getInstanceInfoFromDatabase,
|
||||||
|
setInstanceInfo as setInstanceInfoInDatabase
|
||||||
|
} from '../_database/meta'
|
||||||
|
|
||||||
export function changeTheme (instanceName, newTheme) {
|
export function changeTheme (instanceName, newTheme) {
|
||||||
let instanceThemes = store.get('instanceThemes')
|
let instanceThemes = store.get('instanceThemes')
|
||||||
|
@ -53,7 +59,7 @@ export async function logOutOfInstance (instanceName) {
|
||||||
store.save()
|
store.save()
|
||||||
toast.say(`Logged out of ${instanceName}`)
|
toast.say(`Logged out of ${instanceName}`)
|
||||||
switchToTheme(instanceThemes[newInstance] || 'default')
|
switchToTheme(instanceThemes[newInstance] || 'default')
|
||||||
await database.clearDatabaseForInstance(instanceName)
|
await clearDatabaseForInstance(instanceName)
|
||||||
goto('/settings/instances')
|
goto('/settings/instances')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +74,8 @@ export async function updateVerifyCredentialsForInstance (instanceName) {
|
||||||
let accessToken = loggedInInstances[instanceName].access_token
|
let accessToken = loggedInInstances[instanceName].access_token
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
() => getVerifyCredentials(instanceName, accessToken),
|
() => getVerifyCredentials(instanceName, accessToken),
|
||||||
() => database.getInstanceVerifyCredentials(instanceName),
|
() => getInstanceVerifyCredentialsFromDatabase(instanceName),
|
||||||
verifyCredentials => database.setInstanceVerifyCredentials(instanceName, verifyCredentials),
|
verifyCredentials => setInstanceVerifyCredentialsInDatabase(instanceName, verifyCredentials),
|
||||||
verifyCredentials => setStoreVerifyCredentials(instanceName, verifyCredentials)
|
verifyCredentials => setStoreVerifyCredentials(instanceName, verifyCredentials)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -81,8 +87,8 @@ export async function updateVerifyCredentialsForCurrentInstance () {
|
||||||
export async function updateInstanceInfo (instanceName) {
|
export async function updateInstanceInfo (instanceName) {
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
() => getInstanceInfo(instanceName),
|
() => getInstanceInfo(instanceName),
|
||||||
() => database.getInstanceInfo(instanceName),
|
() => getInstanceInfoFromDatabase(instanceName),
|
||||||
info => database.setInstanceInfo(instanceName, info),
|
info => setInstanceInfoInDatabase(instanceName, info),
|
||||||
info => {
|
info => {
|
||||||
let instanceInfos = store.get('instanceInfos')
|
let instanceInfos = store.get('instanceInfos')
|
||||||
instanceInfos[instanceName] = info
|
instanceInfos[instanceName] = info
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { getLists } from '../_api/lists'
|
import { getLists } from '../_api/lists'
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
|
import {
|
||||||
|
getLists as getListsFromDatabase,
|
||||||
|
setLists as setListsInDatabase
|
||||||
|
} from '../_database/meta'
|
||||||
|
|
||||||
export async function updateLists () {
|
export async function updateLists () {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
|
@ -9,8 +12,8 @@ export async function updateLists () {
|
||||||
|
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
() => getLists(instanceName, accessToken),
|
() => getLists(instanceName, accessToken),
|
||||||
() => database.getLists(instanceName),
|
() => getListsFromDatabase(instanceName),
|
||||||
lists => database.setLists(instanceName, lists),
|
lists => setListsInDatabase(instanceName, lists),
|
||||||
lists => {
|
lists => {
|
||||||
let instanceLists = store.get('instanceLists')
|
let instanceLists = store.get('instanceLists')
|
||||||
instanceLists[instanceName] = lists
|
instanceLists[instanceName] = lists
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
import { getPinnedStatuses } from '../_api/pinnedStatuses'
|
import {
|
||||||
import { database } from '../_database/database'
|
getPinnedStatuses as getPinnedStatusesFromDatabase,
|
||||||
|
insertPinnedStatuses as insertPinnedStatusesInDatabase
|
||||||
|
} from '../_database/timelines/pinnedStatuses'
|
||||||
|
import {
|
||||||
|
getPinnedStatuses
|
||||||
|
} from '../_api/pinnedStatuses'
|
||||||
|
|
||||||
export async function updatePinnedStatusesForAccount (accountId) {
|
export async function updatePinnedStatusesForAccount (accountId) {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
|
@ -9,8 +14,8 @@ export async function updatePinnedStatusesForAccount (accountId) {
|
||||||
|
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
() => getPinnedStatuses(instanceName, accessToken, accountId),
|
() => getPinnedStatuses(instanceName, accessToken, accountId),
|
||||||
() => database.getPinnedStatuses(instanceName, accountId),
|
() => getPinnedStatusesFromDatabase(instanceName, accountId),
|
||||||
statuses => database.insertPinnedStatuses(instanceName, accountId, statuses),
|
statuses => insertPinnedStatusesInDatabase(instanceName, accountId, statuses),
|
||||||
statuses => {
|
statuses => {
|
||||||
let $pinnedStatuses = store.get('pinnedStatuses')
|
let $pinnedStatuses = store.get('pinnedStatuses')
|
||||||
$pinnedStatuses[instanceName] = $pinnedStatuses[instanceName] || {}
|
$pinnedStatuses[instanceName] = $pinnedStatuses[instanceName] || {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { reblogStatus, unreblogStatus } from '../_api/reblog'
|
import { reblogStatus, unreblogStatus } from '../_api/reblog'
|
||||||
|
import { setStatusReblogged as setStatusRebloggedInDatabase } from '../_database/timelines/updateStatus'
|
||||||
|
|
||||||
export async function setReblogged (statusId, reblogged) {
|
export async function setReblogged (statusId, reblogged) {
|
||||||
if (!store.get('online')) {
|
if (!store.get('online')) {
|
||||||
|
@ -16,7 +16,7 @@ export async function setReblogged (statusId, reblogged) {
|
||||||
store.setStatusReblogged(instanceName, statusId, reblogged) // optimistic update
|
store.setStatusReblogged(instanceName, statusId, reblogged) // optimistic update
|
||||||
try {
|
try {
|
||||||
await networkPromise
|
await networkPromise
|
||||||
await database.setStatusReblogged(instanceName, statusId, reblogged)
|
await setStatusRebloggedInDatabase(instanceName, statusId, reblogged)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say(`Failed to ${reblogged ? 'boost' : 'unboost'}. ` + (e.message || ''))
|
toast.say(`Failed to ${reblogged ? 'boost' : 'unboost'}. ` + (e.message || ''))
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
import { database } from '../_database/database'
|
import {
|
||||||
|
getNotificationIdsForStatuses as getNotificationIdsForStatusesFromDatabase,
|
||||||
|
getReblogsForStatus as getReblogsForStatusFromDatabase
|
||||||
|
} from '../_database/timelines/lookup'
|
||||||
|
import {
|
||||||
|
getStatus as getStatusFromDatabase
|
||||||
|
} from '../_database/timelines/getStatusOrNotification'
|
||||||
|
|
||||||
export async function getIdThatThisStatusReblogged (instanceName, statusId) {
|
export async function getIdThatThisStatusReblogged (instanceName, statusId) {
|
||||||
let status = await database.getStatus(instanceName, statusId)
|
let status = await getStatusFromDatabase(instanceName, statusId)
|
||||||
return status.reblog && status.reblog.id
|
return status.reblog && status.reblog.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +19,9 @@ export async function getIdsThatTheseStatusesReblogged (instanceName, statusIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getIdsThatRebloggedThisStatus (instanceName, statusId) {
|
export async function getIdsThatRebloggedThisStatus (instanceName, statusId) {
|
||||||
return database.getReblogsForStatus(instanceName, statusId)
|
return getReblogsForStatusFromDatabase(instanceName, statusId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNotificationIdsForStatuses (instanceName, statusIds) {
|
export async function getNotificationIdsForStatuses (instanceName, statusIds) {
|
||||||
return database.getNotificationIdsForStatuses(instanceName, statusIds)
|
return getNotificationIdsForStatusesFromDatabase(instanceName, statusIds)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { database } from '../_database/database'
|
|
||||||
import { getTimeline } from '../_api/timelines'
|
import { getTimeline } from '../_api/timelines'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { mark, stop } from '../_utils/marks'
|
import { mark, stop } from '../_utils/marks'
|
||||||
import { mergeArrays } from '../_utils/arrays'
|
import { mergeArrays } from '../_utils/arrays'
|
||||||
import { byItemIds } from '../_utils/sorting'
|
import { byItemIds } from '../_utils/sorting'
|
||||||
import isEqual from 'lodash-es/isEqual'
|
import isEqual from 'lodash-es/isEqual'
|
||||||
|
import {
|
||||||
|
insertTimelineItems as insertTimelineItemsInDatabase
|
||||||
|
} from '../_database/timelines/insertion'
|
||||||
|
import {
|
||||||
|
getTimeline as getTimelineFromDatabase
|
||||||
|
} from '../_database/timelines/pagination'
|
||||||
|
|
||||||
const FETCH_LIMIT = 20
|
const FETCH_LIMIT = 20
|
||||||
|
|
||||||
|
@ -14,16 +19,16 @@ async function fetchTimelineItems (instanceName, accessToken, timelineName, last
|
||||||
let items
|
let items
|
||||||
let stale = false
|
let stale = false
|
||||||
if (!online) {
|
if (!online) {
|
||||||
items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
items = await getTimelineFromDatabase(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
||||||
stale = true
|
stale = true
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
items = await getTimeline(instanceName, accessToken, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
items = await getTimeline(instanceName, accessToken, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
||||||
/* no await */ database.insertTimelineItems(instanceName, timelineName, items)
|
/* no await */ insertTimelineItemsInDatabase(instanceName, timelineName, items)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say('Internet request failed. Showing offline content.')
|
toast.say('Internet request failed. Showing offline content.')
|
||||||
items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
items = await getTimelineFromDatabase(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT)
|
||||||
stale = true
|
stale = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,14 @@
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { database } from '../../_database/database'
|
|
||||||
import { insertUsername } from '../../_actions/compose'
|
import { insertUsername } from '../../_actions/compose'
|
||||||
import { insertEmojiAtPosition } from '../../_actions/emoji'
|
import { insertEmojiAtPosition } from '../../_actions/emoji'
|
||||||
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
|
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
|
||||||
import { once } from '../../_utils/once'
|
import { once } from '../../_utils/once'
|
||||||
import ComposeAutosuggestionList from './ComposeAutosuggestionList.html'
|
import ComposeAutosuggestionList from './ComposeAutosuggestionList.html'
|
||||||
|
import {
|
||||||
|
searchAccountsByUsername as searchAccountsByUsernameInDatabase
|
||||||
|
} from '../../_database/accountsAndRelationships'
|
||||||
|
|
||||||
const SEARCH_RESULTS_LIMIT = 4
|
const SEARCH_RESULTS_LIMIT = 4
|
||||||
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
||||||
|
@ -116,7 +118,7 @@
|
||||||
async searchAccounts(searchText) {
|
async searchAccounts(searchText) {
|
||||||
searchText = searchText.substring(1)
|
searchText = searchText.substring(1)
|
||||||
let currentInstance = this.store.get('currentInstance')
|
let currentInstance = this.store.get('currentInstance')
|
||||||
let results = await database.searchAccountsByUsername(
|
let results = await searchAccountsByUsernameInDatabase(
|
||||||
currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT)
|
currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT)
|
||||||
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,7 +45,10 @@
|
||||||
import VirtualList from '../virtualList/VirtualList.html'
|
import VirtualList from '../virtualList/VirtualList.html'
|
||||||
import PseudoVirtualList from '../pseudoVirtualList/PseudoVirtualList.html'
|
import PseudoVirtualList from '../pseudoVirtualList/PseudoVirtualList.html'
|
||||||
import { timelines } from '../../_static/timelines'
|
import { timelines } from '../../_static/timelines'
|
||||||
import { database } from '../../_database/database'
|
import {
|
||||||
|
getStatus as getStatusFromDatabase,
|
||||||
|
getNotification as getNotificationFromDatabase
|
||||||
|
} from '../../_database/timelines/getStatusOrNotification'
|
||||||
import {
|
import {
|
||||||
fetchTimelineItemsOnScrollToBottom,
|
fetchTimelineItemsOnScrollToBottom,
|
||||||
setupTimeline,
|
setupTimeline,
|
||||||
|
@ -87,9 +90,9 @@
|
||||||
timelineValue
|
timelineValue
|
||||||
}
|
}
|
||||||
if (timelineType === 'notifications') {
|
if (timelineType === 'notifications') {
|
||||||
res.notification = await database.getNotification($currentInstance, itemId)
|
res.notification = await getNotificationFromDatabase($currentInstance, itemId)
|
||||||
} else {
|
} else {
|
||||||
res.status = await database.getStatus($currentInstance, itemId)
|
res.status = await getStatusFromDatabase($currentInstance, itemId)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
import * as database from './databaseCore'
|
|
||||||
|
|
||||||
export { database }
|
|
|
@ -1,4 +0,0 @@
|
||||||
export * from './accountsAndRelationships'
|
|
||||||
export * from './clear'
|
|
||||||
export * from './meta'
|
|
||||||
export * from './timelines'
|
|
|
@ -1,470 +0,0 @@
|
||||||
import difference from 'lodash-es/difference'
|
|
||||||
import times from 'lodash-es/times'
|
|
||||||
import { cloneForStorage } from './helpers'
|
|
||||||
import { dbPromise, getDatabase } from './databaseLifecycle'
|
|
||||||
import {
|
|
||||||
accountsCache, deleteFromCache, getInCache, hasInCache, notificationsCache, setInCache,
|
|
||||||
statusesCache
|
|
||||||
} from './cache'
|
|
||||||
import { scheduleCleanup } from './cleanup'
|
|
||||||
import {
|
|
||||||
ACCOUNTS_STORE,
|
|
||||||
NOTIFICATION_TIMELINES_STORE,
|
|
||||||
NOTIFICATIONS_STORE, PINNED_STATUSES_STORE,
|
|
||||||
STATUS_TIMELINES_STORE,
|
|
||||||
STATUSES_STORE,
|
|
||||||
ACCOUNT_ID,
|
|
||||||
REBLOG_ID,
|
|
||||||
STATUS_ID, THREADS_STORE
|
|
||||||
} from './constants'
|
|
||||||
import {
|
|
||||||
createThreadKeyRange,
|
|
||||||
createTimelineKeyRange,
|
|
||||||
createTimelineId,
|
|
||||||
createThreadId,
|
|
||||||
createPinnedStatusKeyRange,
|
|
||||||
createPinnedStatusId
|
|
||||||
} from './keys'
|
|
||||||
import { deleteAll } from './utils'
|
|
||||||
|
|
||||||
function cacheStatus (status, instanceName) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// pagination
|
|
||||||
//
|
|
||||||
|
|
||||||
async function getNotificationTimeline (instanceName, timeline, maxId, limit) {
|
|
||||||
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ timelineStore, notificationsStore, statusesStore, accountsStore ] = stores
|
|
||||||
let keyRange = createTimelineKeyRange(timeline, maxId)
|
|
||||||
|
|
||||||
timelineStore.getAll(keyRange, limit).onsuccess = e => {
|
|
||||||
let timelineResults = e.target.result
|
|
||||||
let res = new Array(timelineResults.length)
|
|
||||||
timelineResults.forEach((notificationId, i) => {
|
|
||||||
fetchNotification(notificationsStore, statusesStore, accountsStore, notificationId, notification => {
|
|
||||||
res[i] = notification
|
|
||||||
})
|
|
||||||
})
|
|
||||||
callback(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getStatusTimeline (instanceName, timeline, maxId, limit) {
|
|
||||||
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ timelineStore, statusesStore, accountsStore ] = stores
|
|
||||||
let getReq = timelineStore.getAll(createTimelineKeyRange(timeline, maxId), limit)
|
|
||||||
getReq.onsuccess = e => {
|
|
||||||
let timelineResults = e.target.result
|
|
||||||
let res = new Array(timelineResults.length)
|
|
||||||
timelineResults.forEach((statusId, i) => {
|
|
||||||
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
|
||||||
res[i] = status
|
|
||||||
})
|
|
||||||
})
|
|
||||||
callback(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getStatusThread (instanceName, statusId) {
|
|
||||||
let storeNames = [THREADS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ threadsStore, statusesStore, accountsStore ] = stores
|
|
||||||
let keyRange = createThreadKeyRange(statusId)
|
|
||||||
threadsStore.getAll(keyRange).onsuccess = e => {
|
|
||||||
let thread = e.target.result
|
|
||||||
if (thread.length) {
|
|
||||||
let res = new Array(thread.length)
|
|
||||||
callback(res)
|
|
||||||
thread.forEach((otherStatusId, i) => {
|
|
||||||
fetchStatus(statusesStore, accountsStore, otherStatusId, status => {
|
|
||||||
res[i] = status
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// thread not cached; just make a "fake" thread with only one status in it
|
|
||||||
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
|
||||||
let res = [status]
|
|
||||||
callback(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getTimeline (instanceName, timeline, maxId = null, limit = 20) {
|
|
||||||
if (timeline === 'notifications') {
|
|
||||||
return getNotificationTimeline(instanceName, timeline, maxId, limit)
|
|
||||||
} else if (timeline.startsWith('status/')) {
|
|
||||||
let statusId = timeline.split('/').slice(-1)[0]
|
|
||||||
return getStatusThread(instanceName, statusId)
|
|
||||||
} else {
|
|
||||||
return getStatusTimeline(instanceName, timeline, maxId, limit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// insertion
|
|
||||||
//
|
|
||||||
|
|
||||||
function putStatus (statusesStore, status) {
|
|
||||||
statusesStore.put(cloneForStorage(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
function putAccount (accountsStore, account) {
|
|
||||||
accountsStore.put(cloneForStorage(account))
|
|
||||||
}
|
|
||||||
|
|
||||||
function putNotification (notificationsStore, notification) {
|
|
||||||
notificationsStore.put(cloneForStorage(notification))
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeAccount (accountsStore, account) {
|
|
||||||
putAccount(accountsStore, account)
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeStatus (statusesStore, accountsStore, status) {
|
|
||||||
putStatus(statusesStore, status)
|
|
||||||
putAccount(accountsStore, status.account)
|
|
||||||
if (status.reblog) {
|
|
||||||
putStatus(statusesStore, status.reblog)
|
|
||||||
putAccount(accountsStore, status.reblog.account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeNotification (notificationsStore, statusesStore, accountsStore, notification) {
|
|
||||||
if (notification.status) {
|
|
||||||
storeStatus(statusesStore, accountsStore, notification.status)
|
|
||||||
}
|
|
||||||
storeAccount(accountsStore, notification.account)
|
|
||||||
putNotification(notificationsStore, notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchAccount (accountsStore, id, callback) {
|
|
||||||
accountsStore.get(id).onsuccess = e => {
|
|
||||||
callback(e.target.result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchStatus (statusesStore, accountsStore, id, callback) {
|
|
||||||
statusesStore.get(id).onsuccess = e => {
|
|
||||||
let status = e.target.result
|
|
||||||
callback(status)
|
|
||||||
fetchAccount(accountsStore, status[ACCOUNT_ID], account => {
|
|
||||||
status.account = account
|
|
||||||
})
|
|
||||||
if (status[REBLOG_ID]) {
|
|
||||||
fetchStatus(statusesStore, accountsStore, status[REBLOG_ID], reblog => {
|
|
||||||
status.reblog = reblog
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchNotification (notificationsStore, statusesStore, accountsStore, id, callback) {
|
|
||||||
notificationsStore.get(id).onsuccess = e => {
|
|
||||||
let notification = e.target.result
|
|
||||||
callback(notification)
|
|
||||||
fetchAccount(accountsStore, notification[ACCOUNT_ID], account => {
|
|
||||||
notification.account = account
|
|
||||||
})
|
|
||||||
if (notification[STATUS_ID]) {
|
|
||||||
fetchStatus(statusesStore, accountsStore, notification[STATUS_ID], status => {
|
|
||||||
notification.status = status
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function insertTimelineNotifications (instanceName, timeline, notifications) {
|
|
||||||
for (let notification of notifications) {
|
|
||||||
setInCache(notificationsCache, instanceName, notification.id, notification)
|
|
||||||
setInCache(accountsCache, instanceName, notification.account.id, notification.account)
|
|
||||||
if (notification.status) {
|
|
||||||
setInCache(statusesCache, instanceName, notification.status.id, notification.status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE, STATUSES_STORE]
|
|
||||||
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
|
||||||
let [ timelineStore, notificationsStore, accountsStore, statusesStore ] = stores
|
|
||||||
for (let notification of notifications) {
|
|
||||||
storeNotification(notificationsStore, statusesStore, accountsStore, notification)
|
|
||||||
timelineStore.put(notification.id, createTimelineId(timeline, notification.id))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function insertTimelineStatuses (instanceName, timeline, statuses) {
|
|
||||||
for (let status of statuses) {
|
|
||||||
cacheStatus(status, instanceName)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
|
||||||
let [ timelineStore, statusesStore, accountsStore ] = stores
|
|
||||||
for (let status of statuses) {
|
|
||||||
storeStatus(statusesStore, accountsStore, status)
|
|
||||||
timelineStore.put(status.id, createTimelineId(timeline, status.id))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function insertStatusThread (instanceName, statusId, statuses) {
|
|
||||||
for (let status of statuses) {
|
|
||||||
cacheStatus(status, instanceName)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [THREADS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
|
||||||
let [ threadsStore, statusesStore, accountsStore ] = stores
|
|
||||||
threadsStore.getAllKeys(createThreadKeyRange(statusId)).onsuccess = e => {
|
|
||||||
let existingKeys = e.target.result
|
|
||||||
let newKeys = times(statuses.length, i => createThreadId(statusId, i))
|
|
||||||
let keysToDelete = difference(existingKeys, newKeys)
|
|
||||||
for (let key of keysToDelete) {
|
|
||||||
threadsStore.delete(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statuses.forEach((otherStatus, i) => {
|
|
||||||
storeStatus(statusesStore, accountsStore, otherStatus)
|
|
||||||
threadsStore.put(otherStatus.id, createThreadId(statusId, i))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function insertTimelineItems (instanceName, timeline, timelineItems) {
|
|
||||||
/* no await */ scheduleCleanup()
|
|
||||||
if (timeline === 'notifications') {
|
|
||||||
return insertTimelineNotifications(instanceName, timeline, timelineItems)
|
|
||||||
} else if (timeline.startsWith('status/')) {
|
|
||||||
let statusId = timeline.split('/').slice(-1)[0]
|
|
||||||
return insertStatusThread(instanceName, statusId, timelineItems)
|
|
||||||
} else {
|
|
||||||
return insertTimelineStatuses(instanceName, timeline, timelineItems)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// get
|
|
||||||
//
|
|
||||||
|
|
||||||
export async function getStatus (instanceName, id) {
|
|
||||||
if (hasInCache(statusesCache, instanceName, id)) {
|
|
||||||
return getInCache(statusesCache, instanceName, id)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
let result = await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ statusesStore, accountsStore ] = stores
|
|
||||||
fetchStatus(statusesStore, accountsStore, id, callback)
|
|
||||||
})
|
|
||||||
setInCache(statusesCache, instanceName, id, result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getNotification (instanceName, id) {
|
|
||||||
if (hasInCache(notificationsCache, instanceName, id)) {
|
|
||||||
return getInCache(notificationsCache, instanceName, id)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [NOTIFICATIONS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
let result = await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ notificationsStore, statusesStore, accountsStore ] = stores
|
|
||||||
fetchNotification(notificationsStore, statusesStore, accountsStore, id, callback)
|
|
||||||
})
|
|
||||||
setInCache(notificationsCache, instanceName, id, result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// lookup by reblogs
|
|
||||||
//
|
|
||||||
|
|
||||||
export async function getReblogsForStatus (instanceName, id) {
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
await dbPromise(db, STATUSES_STORE, 'readonly', (statusesStore, callback) => {
|
|
||||||
statusesStore.index(REBLOG_ID).getAll(IDBKeyRange.only(id)).onsuccess = e => {
|
|
||||||
callback(e.target.result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// lookups by statusId
|
|
||||||
//
|
|
||||||
|
|
||||||
export async function getNotificationIdsForStatuses (instanceName, statusIds) {
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
return dbPromise(db, NOTIFICATIONS_STORE, 'readonly', (notificationsStore, callback) => {
|
|
||||||
let res = []
|
|
||||||
callback(res)
|
|
||||||
statusIds.forEach(statusId => {
|
|
||||||
let req = notificationsStore.index(STATUS_ID).getAllKeys(IDBKeyRange.only(statusId))
|
|
||||||
req.onsuccess = e => {
|
|
||||||
for (let id of e.target.result) {
|
|
||||||
res.push(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// deletes
|
|
||||||
//
|
|
||||||
|
|
||||||
export async function deleteStatusesAndNotifications (instanceName, statusIds, notificationIds) {
|
|
||||||
for (let statusId of statusIds) {
|
|
||||||
deleteFromCache(statusesCache, instanceName, statusId)
|
|
||||||
}
|
|
||||||
for (let notificationId of notificationIds) {
|
|
||||||
deleteFromCache(notificationsCache, instanceName, notificationId)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [
|
|
||||||
STATUSES_STORE,
|
|
||||||
STATUS_TIMELINES_STORE,
|
|
||||||
NOTIFICATIONS_STORE,
|
|
||||||
NOTIFICATION_TIMELINES_STORE,
|
|
||||||
PINNED_STATUSES_STORE,
|
|
||||||
THREADS_STORE
|
|
||||||
]
|
|
||||||
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
|
||||||
let [
|
|
||||||
statusesStore,
|
|
||||||
statusTimelinesStore,
|
|
||||||
notificationsStore,
|
|
||||||
notificationTimelinesStore,
|
|
||||||
pinnedStatusesStore,
|
|
||||||
threadsStore
|
|
||||||
] = stores
|
|
||||||
|
|
||||||
function deleteStatus (statusId) {
|
|
||||||
statusesStore.delete(statusId)
|
|
||||||
deleteAll(
|
|
||||||
pinnedStatusesStore,
|
|
||||||
pinnedStatusesStore.index('statusId'),
|
|
||||||
IDBKeyRange.only(statusId)
|
|
||||||
)
|
|
||||||
deleteAll(
|
|
||||||
statusTimelinesStore,
|
|
||||||
statusTimelinesStore.index('statusId'),
|
|
||||||
IDBKeyRange.only(statusId)
|
|
||||||
)
|
|
||||||
deleteAll(
|
|
||||||
threadsStore,
|
|
||||||
threadsStore.index('statusId'),
|
|
||||||
IDBKeyRange.only(statusId)
|
|
||||||
)
|
|
||||||
deleteAll(
|
|
||||||
threadsStore,
|
|
||||||
threadsStore,
|
|
||||||
createThreadKeyRange(statusId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteNotification (notificationId) {
|
|
||||||
notificationsStore.delete(notificationId)
|
|
||||||
deleteAll(
|
|
||||||
notificationTimelinesStore,
|
|
||||||
notificationTimelinesStore.index('notificationId'),
|
|
||||||
IDBKeyRange.only(notificationId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let statusId of statusIds) {
|
|
||||||
deleteStatus(statusId)
|
|
||||||
}
|
|
||||||
for (let notificationId of notificationIds) {
|
|
||||||
deleteNotification(notificationId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// pinned statuses
|
|
||||||
//
|
|
||||||
|
|
||||||
export async function insertPinnedStatuses (instanceName, accountId, statuses) {
|
|
||||||
for (let status of statuses) {
|
|
||||||
cacheStatus(status, instanceName)
|
|
||||||
}
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
let storeNames = [PINNED_STATUSES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
|
||||||
let [ pinnedStatusesStore, statusesStore, accountsStore ] = stores
|
|
||||||
statuses.forEach((status, i) => {
|
|
||||||
storeStatus(statusesStore, accountsStore, status)
|
|
||||||
pinnedStatusesStore.put(status.id, createPinnedStatusId(accountId, i))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPinnedStatuses (instanceName, accountId) {
|
|
||||||
let storeNames = [PINNED_STATUSES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
|
||||||
let [ pinnedStatusesStore, statusesStore, accountsStore ] = stores
|
|
||||||
let keyRange = createPinnedStatusKeyRange(accountId)
|
|
||||||
pinnedStatusesStore.getAll(keyRange).onsuccess = e => {
|
|
||||||
let pinnedResults = e.target.result
|
|
||||||
let res = new Array(pinnedResults.length)
|
|
||||||
pinnedResults.forEach((statusId, i) => {
|
|
||||||
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
|
||||||
res[i] = status
|
|
||||||
})
|
|
||||||
})
|
|
||||||
callback(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// update statuses
|
|
||||||
//
|
|
||||||
|
|
||||||
async function updateStatus (instanceName, statusId, updateFunc) {
|
|
||||||
const db = await getDatabase(instanceName)
|
|
||||||
if (hasInCache(statusesCache, instanceName, statusId)) {
|
|
||||||
let status = getInCache(statusesCache, instanceName, statusId)
|
|
||||||
updateFunc(status)
|
|
||||||
cacheStatus(status, instanceName)
|
|
||||||
}
|
|
||||||
return dbPromise(db, STATUSES_STORE, 'readwrite', (statusesStore) => {
|
|
||||||
statusesStore.get(statusId).onsuccess = e => {
|
|
||||||
let status = e.target.result
|
|
||||||
updateFunc(status)
|
|
||||||
putStatus(statusesStore, status)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setStatusFavorited (instanceName, statusId, favorited) {
|
|
||||||
return updateStatus(instanceName, statusId, status => {
|
|
||||||
let delta = (favorited ? 1 : 0) - (status.favourited ? 1 : 0)
|
|
||||||
status.favourited = favorited
|
|
||||||
status.favourites_count = (status.favourites_count || 0) + delta
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setStatusReblogged (instanceName, statusId, reblogged) {
|
|
||||||
return updateStatus(instanceName, statusId, status => {
|
|
||||||
let delta = (reblogged ? 1 : 0) - (status.reblogged ? 1 : 0)
|
|
||||||
status.reblogged = reblogged
|
|
||||||
status.reblogs_count = (status.reblogs_count || 0) + delta
|
|
||||||
})
|
|
||||||
}
|
|
11
routes/_database/timelines/cacheStatus.js
Normal file
11
routes/_database/timelines/cacheStatus.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import {
|
||||||
|
accountsCache, setInCache, statusesCache
|
||||||
|
} from '../cache'
|
||||||
|
|
||||||
|
export function cacheStatus (status, instanceName) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
84
routes/_database/timelines/deletion.js
Normal file
84
routes/_database/timelines/deletion.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import {
|
||||||
|
deleteFromCache, notificationsCache,
|
||||||
|
statusesCache
|
||||||
|
} from '../cache'
|
||||||
|
import {
|
||||||
|
NOTIFICATION_TIMELINES_STORE,
|
||||||
|
NOTIFICATIONS_STORE, PINNED_STATUSES_STORE,
|
||||||
|
STATUS_TIMELINES_STORE,
|
||||||
|
STATUSES_STORE,
|
||||||
|
THREADS_STORE
|
||||||
|
} from '../constants'
|
||||||
|
import {
|
||||||
|
createThreadKeyRange
|
||||||
|
} from '../keys'
|
||||||
|
import { deleteAll } from '../utils'
|
||||||
|
|
||||||
|
export async function deleteStatusesAndNotifications (instanceName, statusIds, notificationIds) {
|
||||||
|
for (let statusId of statusIds) {
|
||||||
|
deleteFromCache(statusesCache, instanceName, statusId)
|
||||||
|
}
|
||||||
|
for (let notificationId of notificationIds) {
|
||||||
|
deleteFromCache(notificationsCache, instanceName, notificationId)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [
|
||||||
|
STATUSES_STORE,
|
||||||
|
STATUS_TIMELINES_STORE,
|
||||||
|
NOTIFICATIONS_STORE,
|
||||||
|
NOTIFICATION_TIMELINES_STORE,
|
||||||
|
PINNED_STATUSES_STORE,
|
||||||
|
THREADS_STORE
|
||||||
|
]
|
||||||
|
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
||||||
|
let [
|
||||||
|
statusesStore,
|
||||||
|
statusTimelinesStore,
|
||||||
|
notificationsStore,
|
||||||
|
notificationTimelinesStore,
|
||||||
|
pinnedStatusesStore,
|
||||||
|
threadsStore
|
||||||
|
] = stores
|
||||||
|
|
||||||
|
function deleteStatus (statusId) {
|
||||||
|
statusesStore.delete(statusId)
|
||||||
|
deleteAll(
|
||||||
|
pinnedStatusesStore,
|
||||||
|
pinnedStatusesStore.index('statusId'),
|
||||||
|
IDBKeyRange.only(statusId)
|
||||||
|
)
|
||||||
|
deleteAll(
|
||||||
|
statusTimelinesStore,
|
||||||
|
statusTimelinesStore.index('statusId'),
|
||||||
|
IDBKeyRange.only(statusId)
|
||||||
|
)
|
||||||
|
deleteAll(
|
||||||
|
threadsStore,
|
||||||
|
threadsStore.index('statusId'),
|
||||||
|
IDBKeyRange.only(statusId)
|
||||||
|
)
|
||||||
|
deleteAll(
|
||||||
|
threadsStore,
|
||||||
|
threadsStore,
|
||||||
|
createThreadKeyRange(statusId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteNotification (notificationId) {
|
||||||
|
notificationsStore.delete(notificationId)
|
||||||
|
deleteAll(
|
||||||
|
notificationTimelinesStore,
|
||||||
|
notificationTimelinesStore.index('notificationId'),
|
||||||
|
IDBKeyRange.only(notificationId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let statusId of statusIds) {
|
||||||
|
deleteStatus(statusId)
|
||||||
|
}
|
||||||
|
for (let notificationId of notificationIds) {
|
||||||
|
deleteNotification(notificationId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
5
routes/_database/timelines/fetchAccount.js
Normal file
5
routes/_database/timelines/fetchAccount.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export function fetchAccount (accountsStore, id, callback) {
|
||||||
|
accountsStore.get(id).onsuccess = e => {
|
||||||
|
callback(e.target.result)
|
||||||
|
}
|
||||||
|
}
|
18
routes/_database/timelines/fetchNotification.js
Normal file
18
routes/_database/timelines/fetchNotification.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { fetchAccount } from './fetchAccount'
|
||||||
|
import { ACCOUNT_ID, STATUS_ID } from '../constants'
|
||||||
|
import { fetchStatus } from './fetchStatus'
|
||||||
|
|
||||||
|
export function fetchNotification (notificationsStore, statusesStore, accountsStore, id, callback) {
|
||||||
|
notificationsStore.get(id).onsuccess = e => {
|
||||||
|
let notification = e.target.result
|
||||||
|
callback(notification)
|
||||||
|
fetchAccount(accountsStore, notification[ACCOUNT_ID], account => {
|
||||||
|
notification.account = account
|
||||||
|
})
|
||||||
|
if (notification[STATUS_ID]) {
|
||||||
|
fetchStatus(statusesStore, accountsStore, notification[STATUS_ID], status => {
|
||||||
|
notification.status = status
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
routes/_database/timelines/fetchStatus.js
Normal file
17
routes/_database/timelines/fetchStatus.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { fetchAccount } from './fetchAccount'
|
||||||
|
import { ACCOUNT_ID, REBLOG_ID } from '../constants'
|
||||||
|
|
||||||
|
export function fetchStatus (statusesStore, accountsStore, id, callback) {
|
||||||
|
statusesStore.get(id).onsuccess = e => {
|
||||||
|
let status = e.target.result
|
||||||
|
callback(status)
|
||||||
|
fetchAccount(accountsStore, status[ACCOUNT_ID], account => {
|
||||||
|
status.account = account
|
||||||
|
})
|
||||||
|
if (status[REBLOG_ID]) {
|
||||||
|
fetchStatus(statusesStore, accountsStore, status[REBLOG_ID], reblog => {
|
||||||
|
status.reblog = reblog
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
routes/_database/timelines/getStatusOrNotification.js
Normal file
37
routes/_database/timelines/getStatusOrNotification.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import { getInCache, hasInCache, notificationsCache, setInCache, statusesCache } from '../cache'
|
||||||
|
import {
|
||||||
|
ACCOUNTS_STORE,
|
||||||
|
NOTIFICATIONS_STORE,
|
||||||
|
STATUSES_STORE
|
||||||
|
} from '../constants'
|
||||||
|
import { fetchStatus } from './fetchStatus'
|
||||||
|
import { fetchNotification } from './fetchNotification'
|
||||||
|
|
||||||
|
export async function getStatus (instanceName, id) {
|
||||||
|
if (hasInCache(statusesCache, instanceName, id)) {
|
||||||
|
return getInCache(statusesCache, instanceName, id)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
let result = await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ statusesStore, accountsStore ] = stores
|
||||||
|
fetchStatus(statusesStore, accountsStore, id, callback)
|
||||||
|
})
|
||||||
|
setInCache(statusesCache, instanceName, id, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getNotification (instanceName, id) {
|
||||||
|
if (hasInCache(notificationsCache, instanceName, id)) {
|
||||||
|
return getInCache(notificationsCache, instanceName, id)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [NOTIFICATIONS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
let result = await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ notificationsStore, statusesStore, accountsStore ] = stores
|
||||||
|
fetchNotification(notificationsStore, statusesStore, accountsStore, id, callback)
|
||||||
|
})
|
||||||
|
setInCache(notificationsCache, instanceName, id, result)
|
||||||
|
return result
|
||||||
|
}
|
122
routes/_database/timelines/insertion.js
Normal file
122
routes/_database/timelines/insertion.js
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import difference from 'lodash-es/difference'
|
||||||
|
import times from 'lodash-es/times'
|
||||||
|
import { cloneForStorage } from '../helpers'
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import { accountsCache, notificationsCache, setInCache, statusesCache } from '../cache'
|
||||||
|
import { scheduleCleanup } from '../cleanup'
|
||||||
|
import {
|
||||||
|
ACCOUNTS_STORE,
|
||||||
|
NOTIFICATION_TIMELINES_STORE,
|
||||||
|
NOTIFICATIONS_STORE,
|
||||||
|
STATUS_TIMELINES_STORE,
|
||||||
|
STATUSES_STORE,
|
||||||
|
THREADS_STORE
|
||||||
|
} from '../constants'
|
||||||
|
import {
|
||||||
|
createThreadId,
|
||||||
|
createThreadKeyRange,
|
||||||
|
createTimelineId
|
||||||
|
} from '../keys'
|
||||||
|
import { cacheStatus } from './cacheStatus'
|
||||||
|
|
||||||
|
export function putStatus (statusesStore, status) {
|
||||||
|
statusesStore.put(cloneForStorage(status))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putAccount (accountsStore, account) {
|
||||||
|
accountsStore.put(cloneForStorage(account))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putNotification (notificationsStore, notification) {
|
||||||
|
notificationsStore.put(cloneForStorage(notification))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function storeAccount (accountsStore, account) {
|
||||||
|
putAccount(accountsStore, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function storeStatus (statusesStore, accountsStore, status) {
|
||||||
|
putStatus(statusesStore, status)
|
||||||
|
putAccount(accountsStore, status.account)
|
||||||
|
if (status.reblog) {
|
||||||
|
putStatus(statusesStore, status.reblog)
|
||||||
|
putAccount(accountsStore, status.reblog.account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function storeNotification (notificationsStore, statusesStore, accountsStore, notification) {
|
||||||
|
if (notification.status) {
|
||||||
|
storeStatus(statusesStore, accountsStore, notification.status)
|
||||||
|
}
|
||||||
|
storeAccount(accountsStore, notification.account)
|
||||||
|
putNotification(notificationsStore, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertTimelineNotifications (instanceName, timeline, notifications) {
|
||||||
|
for (let notification of notifications) {
|
||||||
|
setInCache(notificationsCache, instanceName, notification.id, notification)
|
||||||
|
setInCache(accountsCache, instanceName, notification.account.id, notification.account)
|
||||||
|
if (notification.status) {
|
||||||
|
setInCache(statusesCache, instanceName, notification.status.id, notification.status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE, STATUSES_STORE]
|
||||||
|
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
||||||
|
let [ timelineStore, notificationsStore, accountsStore, statusesStore ] = stores
|
||||||
|
for (let notification of notifications) {
|
||||||
|
storeNotification(notificationsStore, statusesStore, accountsStore, notification)
|
||||||
|
timelineStore.put(notification.id, createTimelineId(timeline, notification.id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertTimelineStatuses (instanceName, timeline, statuses) {
|
||||||
|
for (let status of statuses) {
|
||||||
|
cacheStatus(status, instanceName)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
||||||
|
let [ timelineStore, statusesStore, accountsStore ] = stores
|
||||||
|
for (let status of statuses) {
|
||||||
|
storeStatus(statusesStore, accountsStore, status)
|
||||||
|
timelineStore.put(status.id, createTimelineId(timeline, status.id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertStatusThread (instanceName, statusId, statuses) {
|
||||||
|
for (let status of statuses) {
|
||||||
|
cacheStatus(status, instanceName)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [THREADS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
||||||
|
let [ threadsStore, statusesStore, accountsStore ] = stores
|
||||||
|
threadsStore.getAllKeys(createThreadKeyRange(statusId)).onsuccess = e => {
|
||||||
|
let existingKeys = e.target.result
|
||||||
|
let newKeys = times(statuses.length, i => createThreadId(statusId, i))
|
||||||
|
let keysToDelete = difference(existingKeys, newKeys)
|
||||||
|
for (let key of keysToDelete) {
|
||||||
|
threadsStore.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statuses.forEach((otherStatus, i) => {
|
||||||
|
storeStatus(statusesStore, accountsStore, otherStatus)
|
||||||
|
threadsStore.put(otherStatus.id, createThreadId(statusId, i))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertTimelineItems (instanceName, timeline, timelineItems) {
|
||||||
|
/* no await */ scheduleCleanup()
|
||||||
|
if (timeline === 'notifications') {
|
||||||
|
return insertTimelineNotifications(instanceName, timeline, timelineItems)
|
||||||
|
} else if (timeline.startsWith('status/')) {
|
||||||
|
let statusId = timeline.split('/').slice(-1)[0]
|
||||||
|
return insertStatusThread(instanceName, statusId, timelineItems)
|
||||||
|
} else {
|
||||||
|
return insertTimelineStatuses(instanceName, timeline, timelineItems)
|
||||||
|
}
|
||||||
|
}
|
27
routes/_database/timelines/lookup.js
Normal file
27
routes/_database/timelines/lookup.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import { STATUSES_STORE, STATUS_ID, REBLOG_ID, NOTIFICATIONS_STORE } from '../constants'
|
||||||
|
|
||||||
|
export async function getReblogsForStatus (instanceName, id) {
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
await dbPromise(db, STATUSES_STORE, 'readonly', (statusesStore, callback) => {
|
||||||
|
statusesStore.index(REBLOG_ID).getAll(IDBKeyRange.only(id)).onsuccess = e => {
|
||||||
|
callback(e.target.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getNotificationIdsForStatuses (instanceName, statusIds) {
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
return dbPromise(db, NOTIFICATIONS_STORE, 'readonly', (notificationsStore, callback) => {
|
||||||
|
let res = []
|
||||||
|
callback(res)
|
||||||
|
statusIds.forEach(statusId => {
|
||||||
|
let req = notificationsStore.index(STATUS_ID).getAllKeys(IDBKeyRange.only(statusId))
|
||||||
|
req.onsuccess = e => {
|
||||||
|
for (let id of e.target.result) {
|
||||||
|
res.push(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
92
routes/_database/timelines/pagination.js
Normal file
92
routes/_database/timelines/pagination.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import {
|
||||||
|
ACCOUNTS_STORE,
|
||||||
|
NOTIFICATION_TIMELINES_STORE,
|
||||||
|
NOTIFICATIONS_STORE,
|
||||||
|
STATUS_TIMELINES_STORE,
|
||||||
|
STATUSES_STORE,
|
||||||
|
THREADS_STORE
|
||||||
|
} from '../constants'
|
||||||
|
import {
|
||||||
|
createThreadKeyRange,
|
||||||
|
createTimelineKeyRange
|
||||||
|
} from '../keys'
|
||||||
|
import { fetchStatus } from './fetchStatus'
|
||||||
|
import { fetchNotification } from './fetchNotification'
|
||||||
|
|
||||||
|
export async function getNotificationTimeline (instanceName, timeline, maxId, limit) {
|
||||||
|
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ timelineStore, notificationsStore, statusesStore, accountsStore ] = stores
|
||||||
|
let keyRange = createTimelineKeyRange(timeline, maxId)
|
||||||
|
|
||||||
|
timelineStore.getAll(keyRange, limit).onsuccess = e => {
|
||||||
|
let timelineResults = e.target.result
|
||||||
|
let res = new Array(timelineResults.length)
|
||||||
|
timelineResults.forEach((notificationId, i) => {
|
||||||
|
fetchNotification(notificationsStore, statusesStore, accountsStore, notificationId, notification => {
|
||||||
|
res[i] = notification
|
||||||
|
})
|
||||||
|
})
|
||||||
|
callback(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStatusTimeline (instanceName, timeline, maxId, limit) {
|
||||||
|
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ timelineStore, statusesStore, accountsStore ] = stores
|
||||||
|
let getReq = timelineStore.getAll(createTimelineKeyRange(timeline, maxId), limit)
|
||||||
|
getReq.onsuccess = e => {
|
||||||
|
let timelineResults = e.target.result
|
||||||
|
let res = new Array(timelineResults.length)
|
||||||
|
timelineResults.forEach((statusId, i) => {
|
||||||
|
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
||||||
|
res[i] = status
|
||||||
|
})
|
||||||
|
})
|
||||||
|
callback(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStatusThread (instanceName, statusId) {
|
||||||
|
let storeNames = [THREADS_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ threadsStore, statusesStore, accountsStore ] = stores
|
||||||
|
let keyRange = createThreadKeyRange(statusId)
|
||||||
|
threadsStore.getAll(keyRange).onsuccess = e => {
|
||||||
|
let thread = e.target.result
|
||||||
|
if (thread.length) {
|
||||||
|
let res = new Array(thread.length)
|
||||||
|
callback(res)
|
||||||
|
thread.forEach((otherStatusId, i) => {
|
||||||
|
fetchStatus(statusesStore, accountsStore, otherStatusId, status => {
|
||||||
|
res[i] = status
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// thread not cached; just make a "fake" thread with only one status in it
|
||||||
|
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
||||||
|
let res = [status]
|
||||||
|
callback(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTimeline (instanceName, timeline, maxId = null, limit = 20) {
|
||||||
|
if (timeline === 'notifications') {
|
||||||
|
return getNotificationTimeline(instanceName, timeline, maxId, limit)
|
||||||
|
} else if (timeline.startsWith('status/')) {
|
||||||
|
let statusId = timeline.split('/').slice(-1)[0]
|
||||||
|
return getStatusThread(instanceName, statusId)
|
||||||
|
} else {
|
||||||
|
return getStatusTimeline(instanceName, timeline, maxId, limit)
|
||||||
|
}
|
||||||
|
}
|
40
routes/_database/timelines/pinnedStatuses.js
Normal file
40
routes/_database/timelines/pinnedStatuses.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { cacheStatus } from './cacheStatus'
|
||||||
|
import { getDatabase, dbPromise } from '../databaseLifecycle'
|
||||||
|
import { PINNED_STATUSES_STORE, STATUSES_STORE, ACCOUNTS_STORE } from '../constants'
|
||||||
|
import { createPinnedStatusId, createPinnedStatusKeyRange } from '../keys'
|
||||||
|
import { storeStatus } from './insertion'
|
||||||
|
import { fetchStatus } from './fetchStatus'
|
||||||
|
|
||||||
|
export async function insertPinnedStatuses (instanceName, accountId, statuses) {
|
||||||
|
for (let status of statuses) {
|
||||||
|
cacheStatus(status, instanceName)
|
||||||
|
}
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
let storeNames = [PINNED_STATUSES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
await dbPromise(db, storeNames, 'readwrite', (stores) => {
|
||||||
|
let [ pinnedStatusesStore, statusesStore, accountsStore ] = stores
|
||||||
|
statuses.forEach((status, i) => {
|
||||||
|
storeStatus(statusesStore, accountsStore, status)
|
||||||
|
pinnedStatusesStore.put(status.id, createPinnedStatusId(accountId, i))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPinnedStatuses (instanceName, accountId) {
|
||||||
|
let storeNames = [PINNED_STATUSES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
|
let [ pinnedStatusesStore, statusesStore, accountsStore ] = stores
|
||||||
|
let keyRange = createPinnedStatusKeyRange(accountId)
|
||||||
|
pinnedStatusesStore.getAll(keyRange).onsuccess = e => {
|
||||||
|
let pinnedResults = e.target.result
|
||||||
|
let res = new Array(pinnedResults.length)
|
||||||
|
pinnedResults.forEach((statusId, i) => {
|
||||||
|
fetchStatus(statusesStore, accountsStore, statusId, status => {
|
||||||
|
res[i] = status
|
||||||
|
})
|
||||||
|
})
|
||||||
|
callback(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
41
routes/_database/timelines/updateStatus.js
Normal file
41
routes/_database/timelines/updateStatus.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { dbPromise, getDatabase } from '../databaseLifecycle'
|
||||||
|
import { getInCache, hasInCache, statusesCache } from '../cache'
|
||||||
|
import { STATUSES_STORE } from '../constants'
|
||||||
|
import { cacheStatus } from './cacheStatus'
|
||||||
|
import { putStatus } from './insertion'
|
||||||
|
|
||||||
|
//
|
||||||
|
// update statuses
|
||||||
|
//
|
||||||
|
|
||||||
|
async function updateStatus (instanceName, statusId, updateFunc) {
|
||||||
|
const db = await getDatabase(instanceName)
|
||||||
|
if (hasInCache(statusesCache, instanceName, statusId)) {
|
||||||
|
let status = getInCache(statusesCache, instanceName, statusId)
|
||||||
|
updateFunc(status)
|
||||||
|
cacheStatus(status, instanceName)
|
||||||
|
}
|
||||||
|
return dbPromise(db, STATUSES_STORE, 'readwrite', (statusesStore) => {
|
||||||
|
statusesStore.get(statusId).onsuccess = e => {
|
||||||
|
let status = e.target.result
|
||||||
|
updateFunc(status)
|
||||||
|
putStatus(statusesStore, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setStatusFavorited (instanceName, statusId, favorited) {
|
||||||
|
return updateStatus(instanceName, statusId, status => {
|
||||||
|
let delta = (favorited ? 1 : 0) - (status.favourited ? 1 : 0)
|
||||||
|
status.favourited = favorited
|
||||||
|
status.favourites_count = (status.favourites_count || 0) + delta
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setStatusReblogged (instanceName, statusId, reblogged) {
|
||||||
|
return updateStatus(instanceName, statusId, status => {
|
||||||
|
let delta = (reblogged ? 1 : 0) - (status.reblogged ? 1 : 0)
|
||||||
|
status.reblogged = reblogged
|
||||||
|
status.reblogs_count = (status.reblogs_count || 0) + delta
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue