run idb operations in a web worker (#517)
This commit is contained in:
		
							parent
							
								
									2449a27767
								
							
						
					
					
						commit
						d599f2f308
					
				
					 30 changed files with 316 additions and 124 deletions
				
			
		
							
								
								
									
										39
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										39
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -2243,13 +2243,11 @@ | |||
|             }, | ||||
|             "balanced-match": { | ||||
|               "version": "1.0.0", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "brace-expansion": { | ||||
|               "version": "1.1.11", | ||||
|               "bundled": true, | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "balanced-match": "1.0.0", | ||||
|                 "concat-map": "0.0.1" | ||||
|  | @ -2262,18 +2260,15 @@ | |||
|             }, | ||||
|             "code-point-at": { | ||||
|               "version": "1.1.0", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "concat-map": { | ||||
|               "version": "0.0.1", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "console-control-strings": { | ||||
|               "version": "1.1.0", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "core-util-is": { | ||||
|               "version": "1.0.2", | ||||
|  | @ -2376,8 +2371,7 @@ | |||
|             }, | ||||
|             "inherits": { | ||||
|               "version": "2.0.3", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "ini": { | ||||
|               "version": "1.3.5", | ||||
|  | @ -2387,7 +2381,6 @@ | |||
|             "is-fullwidth-code-point": { | ||||
|               "version": "1.0.0", | ||||
|               "bundled": true, | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "number-is-nan": "1.0.1" | ||||
|               } | ||||
|  | @ -2400,15 +2393,13 @@ | |||
|             "minimatch": { | ||||
|               "version": "3.0.4", | ||||
|               "bundled": true, | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "brace-expansion": "1.1.11" | ||||
|               } | ||||
|             }, | ||||
|             "minimist": { | ||||
|               "version": "0.0.8", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "minipass": { | ||||
|               "version": "2.2.4", | ||||
|  | @ -2429,7 +2420,6 @@ | |||
|             "mkdirp": { | ||||
|               "version": "0.5.1", | ||||
|               "bundled": true, | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "minimist": "0.0.8" | ||||
|               } | ||||
|  | @ -2502,8 +2492,7 @@ | |||
|             }, | ||||
|             "number-is-nan": { | ||||
|               "version": "1.0.1", | ||||
|               "bundled": true, | ||||
|               "optional": true | ||||
|               "bundled": true | ||||
|             }, | ||||
|             "object-assign": { | ||||
|               "version": "4.1.1", | ||||
|  | @ -2618,7 +2607,6 @@ | |||
|             "string-width": { | ||||
|               "version": "1.0.2", | ||||
|               "bundled": true, | ||||
|               "optional": true, | ||||
|               "requires": { | ||||
|                 "code-point-at": "1.1.0", | ||||
|                 "is-fullwidth-code-point": "1.0.0", | ||||
|  | @ -6697,6 +6685,11 @@ | |||
|         "postcss": "^6.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "idb-keyval": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-3.1.0.tgz", | ||||
|       "integrity": "sha512-iFwFN5n00KNNnVxlOOK280SJJfXWY7pbMUOQXdIXehvvc/mGCV/6T2Ae+Pk2KwAkkATDTwfMavOiDH5lrJKWXQ==" | ||||
|     }, | ||||
|     "ieee754": { | ||||
|       "version": "1.1.12", | ||||
|       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", | ||||
|  | @ -12594,6 +12587,14 @@ | |||
|         "errno": "~0.1.7" | ||||
|       } | ||||
|     }, | ||||
|     "workerize-loader": { | ||||
|       "version": "1.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/workerize-loader/-/workerize-loader-1.0.4.tgz", | ||||
|       "integrity": "sha512-HMTr/zpuZhm8dbhcK52cMYmn57uf7IJeMZJil+5lL/vC5+AO9wzxZ0FISkGVj78No7HcpaINwAWHGCYx3dnsTw==", | ||||
|       "requires": { | ||||
|         "loader-utils": "^1.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "wrap-ansi": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", | ||||
|  |  | |||
|  | @ -66,6 +66,7 @@ | |||
|     "form-data": "^2.3.2", | ||||
|     "glob": "^7.1.2", | ||||
|     "helmet": "^3.13.0", | ||||
|     "idb-keyval": "^3.1.0", | ||||
|     "indexeddb-getall-shim": "^1.3.5", | ||||
|     "intersection-observer": "^0.5.0", | ||||
|     "lodash-es": "^4.17.10", | ||||
|  | @ -98,6 +99,7 @@ | |||
|     "web-animations-js": "^2.3.1", | ||||
|     "webpack": "^4.17.1", | ||||
|     "webpack-bundle-analyzer": "^2.13.1", | ||||
|     "workerize-loader": "^1.0.4", | ||||
|     "yargs": "^12.0.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|  |  | |||
|  | @ -1,18 +1,12 @@ | |||
| import { getAccount } from '../_api/user' | ||||
| import { getRelationship } from '../_api/relationships' | ||||
| import { | ||||
|   getAccount as getAccountFromDatabase, | ||||
|   setAccount as setAccountInDatabase} from '../_database/accounts' | ||||
| import { | ||||
|   getRelationship as getRelationshipFromDatabase, | ||||
|   setRelationship as setRelationshipInDatabase | ||||
| } from '../_database/relationships' | ||||
| import { database } from '../_database/database' | ||||
| import { store } from '../_store/store' | ||||
| 
 | ||||
| async function _updateAccount (accountId, instanceName, accessToken) { | ||||
|   let localPromise = getAccountFromDatabase(instanceName, accountId) | ||||
|   let localPromise = database.getAccount(instanceName, accountId) | ||||
|   let remotePromise = getAccount(instanceName, accessToken, accountId).then(account => { | ||||
|     /* no await */ setAccountInDatabase(instanceName, account) | ||||
|     /* no await */ database.setAccount(instanceName, account) | ||||
|     return account | ||||
|   }) | ||||
| 
 | ||||
|  | @ -29,9 +23,9 @@ async function _updateAccount (accountId, instanceName, accessToken) { | |||
| } | ||||
| 
 | ||||
| async function _updateRelationship (accountId, instanceName, accessToken) { | ||||
|   let localPromise = getRelationshipFromDatabase(instanceName, accountId) | ||||
|   let localPromise = database.getRelationship(instanceName, accountId) | ||||
|   let remotePromise = getRelationship(instanceName, accessToken, accountId).then(relationship => { | ||||
|     /* no await */ setRelationshipInDatabase(instanceName, relationship) | ||||
|     /* no await */ database.setRelationship(instanceName, relationship) | ||||
|     return relationship | ||||
|   }) | ||||
|   try { | ||||
|  | @ -47,7 +41,7 @@ async function _updateRelationship (accountId, instanceName, accessToken) { | |||
| } | ||||
| 
 | ||||
| export async function updateLocalRelationship (instanceName, accountId, relationship) { | ||||
|   await setRelationshipInDatabase(instanceName, relationship) | ||||
|   await database.setRelationship(instanceName, relationship) | ||||
|   try { | ||||
|     store.set({currentAccountRelationship: relationship}) | ||||
|   } catch (e) { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import { switchToTheme } from '../_utils/themeEngine' | |||
| import { store } from '../_store/store' | ||||
| import { updateVerifyCredentialsForInstance } from './instances' | ||||
| import { updateCustomEmojiForInstance } from './emoji' | ||||
| import { setInstanceInfo as setInstanceInfoInDatabase } from '../_database/meta' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| const REDIRECT_URI = (typeof location !== 'undefined' | ||||
|   ? location.origin : 'https://pinafore.social') + '/settings/instances/add' | ||||
|  | @ -19,7 +19,7 @@ async function redirectToOauth () { | |||
|   } | ||||
|   let registrationPromise = registerApplication(instanceNameInSearch, REDIRECT_URI) | ||||
|   let instanceInfo = await getInstanceInfo(instanceNameInSearch) | ||||
|   await setInstanceInfoInDatabase(instanceNameInSearch, instanceInfo) // cache for later
 | ||||
|   await database.setInstanceInfo(instanceNameInSearch, instanceInfo) // cache for later
 | ||||
|   let instanceData = await registrationPromise | ||||
|   store.set({ | ||||
|     currentRegisteredInstanceName: instanceNameInSearch, | ||||
|  |  | |||
|  | @ -4,9 +4,7 @@ import { store } from '../_store/store' | |||
| import uniqBy from 'lodash-es/uniqBy' | ||||
| import uniq from 'lodash-es/uniq' | ||||
| import isEqual from 'lodash-es/isEqual' | ||||
| import { | ||||
|   insertTimelineItems as insertTimelineItemsInDatabase | ||||
| } from '../_database/timelines/insertion' | ||||
| import { database } from '../_database/database' | ||||
| import { runMediumPriorityTask } from '../_utils/runMediumPriorityTask' | ||||
| 
 | ||||
| const STREAMING_THROTTLE_DELAY = 3000 | ||||
|  | @ -29,7 +27,7 @@ async function insertUpdatesIntoTimeline (instanceName, timelineName, updates) { | |||
|     return | ||||
|   } | ||||
| 
 | ||||
|   await insertTimelineItemsInDatabase(instanceName, timelineName, updates) | ||||
|   await database.insertTimelineItems(instanceName, timelineName, updates) | ||||
| 
 | ||||
|   let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || [] | ||||
|   let newItemIdsToAdd = uniq([].concat(itemIdsToAdd).concat(updates.map(_ => _.id))) | ||||
|  |  | |||
|  | @ -2,13 +2,13 @@ import { store } from '../_store/store' | |||
| import { toast } from '../_utils/toast' | ||||
| import { postStatus as postStatusToServer } from '../_api/statuses' | ||||
| import { addStatusOrNotification } from './addStatusOrNotification' | ||||
| import { getStatus as getStatusFromDatabase } from '../_database/timelines/getStatusOrNotification' | ||||
| import { database } from '../_database/database' | ||||
| import { emit } from '../_utils/eventBus' | ||||
| import { putMediaDescription } from '../_api/media' | ||||
| 
 | ||||
| export async function insertHandleForReply (statusId) { | ||||
|   let { currentInstance } = store.get() | ||||
|   let status = await getStatusFromDatabase(currentInstance, statusId) | ||||
|   let status = await database.getStatus(currentInstance, statusId) | ||||
|   let { currentVerifyCredentials } = store.get() | ||||
|   let originalStatus = status.reblog || status | ||||
|   let accounts = [originalStatus.account].concat(originalStatus.mentions || []) | ||||
|  |  | |||
|  | @ -2,9 +2,7 @@ import { getIdsThatRebloggedThisStatus, getNotificationIdsForStatuses } from './ | |||
| import { store } from '../_store/store' | ||||
| import { scheduleIdleTask } from '../_utils/scheduleIdleTask' | ||||
| import isEqual from 'lodash-es/isEqual' | ||||
| import { | ||||
|   deleteStatusesAndNotifications as deleteStatusesAndNotificationsFromDatabase | ||||
| } from '../_database/timelines/deletion' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| function filterItemIdsFromTimelines (instanceName, timelineFilter, idFilter) { | ||||
|   let keys = ['timelineItemIds', 'itemIdsToAdd'] | ||||
|  | @ -45,7 +43,7 @@ function deleteNotificationIdsFromStore (instanceName, idsToDelete) { | |||
| async function deleteStatusesAndNotifications (instanceName, statusIdsToDelete, notificationIdsToDelete) { | ||||
|   deleteStatusIdsFromStore(instanceName, statusIdsToDelete) | ||||
|   deleteNotificationIdsFromStore(instanceName, notificationIdsToDelete) | ||||
|   await deleteStatusesAndNotificationsFromDatabase(instanceName, statusIdsToDelete, notificationIdsToDelete) | ||||
|   await database.deleteStatusesAndNotifications(instanceName, statusIdsToDelete, notificationIdsToDelete) | ||||
| } | ||||
| 
 | ||||
| async function doDeleteStatus (instanceName, statusId) { | ||||
|  |  | |||
|  | @ -1,16 +1,13 @@ | |||
| import { cacheFirstUpdateAfter } from '../_utils/sync' | ||||
| import { | ||||
|   getCustomEmoji as getCustomEmojiFromDatabase, | ||||
|   setCustomEmoji as setCustomEmojiInDatabase | ||||
| } from '../_database/meta' | ||||
| import { database } from '../_database/database' | ||||
| import { getCustomEmoji } from '../_api/emoji' | ||||
| import { store } from '../_store/store' | ||||
| 
 | ||||
| export async function updateCustomEmojiForInstance (instanceName) { | ||||
|   await cacheFirstUpdateAfter( | ||||
|     () => getCustomEmoji(instanceName), | ||||
|     () => getCustomEmojiFromDatabase(instanceName), | ||||
|     emoji => setCustomEmojiInDatabase(instanceName, emoji), | ||||
|     () => database.getCustomEmoji(instanceName), | ||||
|     emoji => database.setCustomEmoji(instanceName, emoji), | ||||
|     emoji => { | ||||
|       let { customEmoji } = store.get() | ||||
|       customEmoji[instanceName] = emoji | ||||
|  |  | |||
|  | @ -1,9 +1,7 @@ | |||
| import { favoriteStatus, unfavoriteStatus } from '../_api/favorite' | ||||
| import { store } from '../_store/store' | ||||
| import { toast } from '../_utils/toast' | ||||
| import { | ||||
|   setStatusFavorited as setStatusFavoritedInDatabase | ||||
| } from '../_database/timelines/updateStatus' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export async function setFavorited (statusId, favorited) { | ||||
|   let { online } = store.get() | ||||
|  | @ -18,7 +16,7 @@ export async function setFavorited (statusId, favorited) { | |||
|   store.setStatusFavorited(currentInstance, statusId, favorited) // optimistic update
 | ||||
|   try { | ||||
|     await networkPromise | ||||
|     await setStatusFavoritedInDatabase(currentInstance, statusId, favorited) | ||||
|     await database.setStatusFavorited(currentInstance, statusId, favorited) | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|     toast.say(`Failed to ${favorited ? 'favorite' : 'unfavorite'}. ` + (e.message || '')) | ||||
|  |  | |||
|  | @ -5,13 +5,7 @@ import { toast } from '../_utils/toast' | |||
| import { goto } from 'sapper/runtime.js' | ||||
| import { cacheFirstUpdateAfter } from '../_utils/sync' | ||||
| 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' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export function changeTheme (instanceName, newTheme) { | ||||
|   let { instanceThemes } = store.get() | ||||
|  | @ -62,7 +56,7 @@ export async function logOutOfInstance (instanceName) { | |||
|   store.save() | ||||
|   toast.say(`Logged out of ${instanceName}`) | ||||
|   switchToTheme(instanceThemes[newInstance] || 'default') | ||||
|   await clearDatabaseForInstance(instanceName) | ||||
|   await database.clearDatabaseForInstance(instanceName) | ||||
|   goto('/settings/instances') | ||||
| } | ||||
| 
 | ||||
|  | @ -77,8 +71,8 @@ export async function updateVerifyCredentialsForInstance (instanceName) { | |||
|   let accessToken = loggedInInstances[instanceName].access_token | ||||
|   await cacheFirstUpdateAfter( | ||||
|     () => getVerifyCredentials(instanceName, accessToken), | ||||
|     () => getInstanceVerifyCredentialsFromDatabase(instanceName), | ||||
|     verifyCredentials => setInstanceVerifyCredentialsInDatabase(instanceName, verifyCredentials), | ||||
|     () => database.getInstanceVerifyCredentials(instanceName), | ||||
|     verifyCredentials => database.setInstanceVerifyCredentials(instanceName, verifyCredentials), | ||||
|     verifyCredentials => setStoreVerifyCredentials(instanceName, verifyCredentials) | ||||
|   ) | ||||
| } | ||||
|  | @ -91,8 +85,8 @@ export async function updateVerifyCredentialsForCurrentInstance () { | |||
| export async function updateInstanceInfo (instanceName) { | ||||
|   await cacheFirstUpdateAfter( | ||||
|     () => getInstanceInfo(instanceName), | ||||
|     () => getInstanceInfoFromDatabase(instanceName), | ||||
|     info => setInstanceInfoInDatabase(instanceName, info), | ||||
|     () => database.getInstanceInfo(instanceName), | ||||
|     info => database.setInstanceInfo(instanceName, info), | ||||
|     info => { | ||||
|       let { instanceInfos } = store.get() | ||||
|       instanceInfos[instanceName] = info | ||||
|  |  | |||
|  | @ -1,18 +1,15 @@ | |||
| import { store } from '../_store/store' | ||||
| import { getLists } from '../_api/lists' | ||||
| import { cacheFirstUpdateAfter } from '../_utils/sync' | ||||
| import { | ||||
|   getLists as getListsFromDatabase, | ||||
|   setLists as setListsInDatabase | ||||
| } from '../_database/meta' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export async function updateLists () { | ||||
|   let { currentInstance, accessToken } = store.get() | ||||
| 
 | ||||
|   await cacheFirstUpdateAfter( | ||||
|     () => getLists(currentInstance, accessToken), | ||||
|     () => getListsFromDatabase(currentInstance), | ||||
|     lists => setListsInDatabase(currentInstance, lists), | ||||
|     () => database.getLists(currentInstance), | ||||
|     lists => database.setLists(currentInstance, lists), | ||||
|     lists => { | ||||
|       let { instanceLists } = store.get() | ||||
|       instanceLists[currentInstance] = lists | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { store } from '../_store/store' | ||||
| import { muteConversation, unmuteConversation } from '../_api/muteConversation' | ||||
| import { toast } from '../_utils/toast' | ||||
| import { setStatusMuted as setStatusMutedInDatabase } from '../_database/timelines/updateStatus' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export async function setConversationMuted (statusId, mute, toastOnSuccess) { | ||||
|   let { currentInstance, accessToken } = store.get() | ||||
|  | @ -11,7 +11,7 @@ export async function setConversationMuted (statusId, mute, toastOnSuccess) { | |||
|     } else { | ||||
|       await unmuteConversation(currentInstance, accessToken, statusId) | ||||
|     } | ||||
|     await setStatusMutedInDatabase(currentInstance, statusId, mute) | ||||
|     await database.setStatusMuted(currentInstance, statusId, mute) | ||||
|     if (toastOnSuccess) { | ||||
|       if (mute) { | ||||
|         toast.say('Muted conversation') | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { store } from '../_store/store' | ||||
| import { toast } from '../_utils/toast' | ||||
| import { pinStatus, unpinStatus } from '../_api/pin' | ||||
| import { setStatusPinned as setStatusPinnedInDatabase } from '../_database/timelines/updateStatus' | ||||
| import { database } from '../_database/database' | ||||
| import { emit } from '../_utils/eventBus' | ||||
| 
 | ||||
| export async function setStatusPinnedOrUnpinned (statusId, pinned, toastOnSuccess) { | ||||
|  | @ -20,7 +20,7 @@ export async function setStatusPinnedOrUnpinned (statusId, pinned, toastOnSucces | |||
|       } | ||||
|     } | ||||
|     store.setStatusPinned(currentInstance, statusId, pinned) | ||||
|     await setStatusPinnedInDatabase(currentInstance, statusId, pinned) | ||||
|     await database.setStatusPinned(currentInstance, statusId, pinned) | ||||
|     emit('updatePinnedStatuses') | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|  |  | |||
|  | @ -1,9 +1,6 @@ | |||
| import { store } from '../_store/store' | ||||
| import { cacheFirstUpdateAfter } from '../_utils/sync' | ||||
| import { | ||||
|   getPinnedStatuses as getPinnedStatusesFromDatabase, | ||||
|   insertPinnedStatuses as insertPinnedStatusesInDatabase | ||||
| } from '../_database/timelines/pinnedStatuses' | ||||
| import { database } from '../_database/database' | ||||
| import { | ||||
|   getPinnedStatuses | ||||
| } from '../_api/pinnedStatuses' | ||||
|  | @ -13,8 +10,8 @@ export async function updatePinnedStatusesForAccount (accountId) { | |||
| 
 | ||||
|   await cacheFirstUpdateAfter( | ||||
|     () => getPinnedStatuses(currentInstance, accessToken, accountId), | ||||
|     () => getPinnedStatusesFromDatabase(currentInstance, accountId), | ||||
|     statuses => insertPinnedStatusesInDatabase(currentInstance, accountId, statuses), | ||||
|     () => database.getPinnedStatuses(currentInstance, accountId), | ||||
|     statuses => database.insertPinnedStatuses(currentInstance, accountId, statuses), | ||||
|     statuses => { | ||||
|       let { pinnedStatuses } = store.get() | ||||
|       pinnedStatuses[currentInstance] = pinnedStatuses[currentInstance] || {} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { store } from '../_store/store' | ||||
| import { toast } from '../_utils/toast' | ||||
| import { reblogStatus, unreblogStatus } from '../_api/reblog' | ||||
| import { setStatusReblogged as setStatusRebloggedInDatabase } from '../_database/timelines/updateStatus' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export async function setReblogged (statusId, reblogged) { | ||||
|   let online = store.get() | ||||
|  | @ -16,7 +16,7 @@ export async function setReblogged (statusId, reblogged) { | |||
|   store.setStatusReblogged(currentInstance, statusId, reblogged) // optimistic update
 | ||||
|   try { | ||||
|     await networkPromise | ||||
|     await setStatusRebloggedInDatabase(currentInstance, statusId, reblogged) | ||||
|     await database.setStatusReblogged(currentInstance, statusId, reblogged) | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|     toast.say(`Failed to ${reblogged ? 'boost' : 'unboost'}. ` + (e.message || '')) | ||||
|  |  | |||
|  | @ -1,13 +1,7 @@ | |||
| import { | ||||
|   getNotificationIdsForStatuses as getNotificationIdsForStatusesFromDatabase, | ||||
|   getReblogsForStatus as getReblogsForStatusFromDatabase | ||||
| } from '../_database/timelines/lookup' | ||||
| import { | ||||
|   getStatus as getStatusFromDatabase | ||||
| } from '../_database/timelines/getStatusOrNotification' | ||||
| import { database } from '../_database/database' | ||||
| 
 | ||||
| export async function getIdThatThisStatusReblogged (instanceName, statusId) { | ||||
|   let status = await getStatusFromDatabase(instanceName, statusId) | ||||
|   let status = await database.getStatus(instanceName, statusId) | ||||
|   return status.reblog && status.reblog.id | ||||
| } | ||||
| 
 | ||||
|  | @ -19,9 +13,9 @@ export async function getIdsThatTheseStatusesReblogged (instanceName, statusIds) | |||
| } | ||||
| 
 | ||||
| export async function getIdsThatRebloggedThisStatus (instanceName, statusId) { | ||||
|   return getReblogsForStatusFromDatabase(instanceName, statusId) | ||||
|   return database.getReblogsForStatus(instanceName, statusId) | ||||
| } | ||||
| 
 | ||||
| export async function getNotificationIdsForStatuses (instanceName, statusIds) { | ||||
|   return getNotificationIdsForStatusesFromDatabase(instanceName, statusIds) | ||||
|   return database.getNotificationIdsForStatuses(instanceName, statusIds) | ||||
| } | ||||
|  |  | |||
|  | @ -5,19 +5,14 @@ import { mark, stop } from '../_utils/marks' | |||
| import { concat, mergeArrays } from '../_utils/arrays' | ||||
| import { byItemIds } from '../_utils/sorting' | ||||
| import isEqual from 'lodash-es/isEqual' | ||||
| import { | ||||
|   insertTimelineItems as insertTimelineItemsInDatabase | ||||
| } from '../_database/timelines/insertion' | ||||
| import { | ||||
|   getTimeline as getTimelineFromDatabase | ||||
| } from '../_database/timelines/pagination' | ||||
| import { database } from '../_database/database' | ||||
| import { getStatus, getStatusContext } from '../_api/statuses' | ||||
| import { emit } from '../_utils/eventBus' | ||||
| 
 | ||||
| const FETCH_LIMIT = 20 | ||||
| 
 | ||||
| async function storeFreshTimelineItemsInDatabase (instanceName, timelineName, items) { | ||||
|   await insertTimelineItemsInDatabase(instanceName, timelineName, items) | ||||
|   await database.insertTimelineItems(instanceName, timelineName, items) | ||||
|   if (timelineName.startsWith('status/')) { | ||||
|     // For status threads, we want to be sure to update the favorite/reblog counts even if
 | ||||
|     // this is a stale "view" of the status. See 119-status-counts-update.js for
 | ||||
|  | @ -45,7 +40,7 @@ async function fetchTimelineItems (instanceName, accessToken, timelineName, last | |||
|   let items | ||||
|   let stale = false | ||||
|   if (!online) { | ||||
|     items = await getTimelineFromDatabase(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT) | ||||
|     items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT) | ||||
|     stale = true | ||||
|   } else { | ||||
|     try { | ||||
|  | @ -54,7 +49,7 @@ async function fetchTimelineItems (instanceName, accessToken, timelineName, last | |||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|       toast.say('Internet request failed. Showing offline content.') | ||||
|       items = await getTimelineFromDatabase(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT) | ||||
|       items = await database.getTimeline(instanceName, timelineName, lastTimelineItemId, FETCH_LIMIT) | ||||
|       stale = true | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -41,10 +41,7 @@ | |||
|     importNotificationVirtualListItem | ||||
|   } from '../../_utils/asyncModules' | ||||
|   import { timelines } from '../../_static/timelines' | ||||
|   import { | ||||
|     getStatus as getStatusFromDatabase, | ||||
|     getNotification as getNotificationFromDatabase | ||||
|   } from '../../_database/timelines/getStatusOrNotification' | ||||
|   import { database } from '../../_database/database' | ||||
|   import { | ||||
|     fetchTimelineItemsOnScrollToBottom, | ||||
|     setupTimeline, | ||||
|  | @ -101,9 +98,9 @@ | |||
|           timelineValue | ||||
|         } | ||||
|         if (timelineType === 'notifications') { | ||||
|           res.notification = await getNotificationFromDatabase($currentInstance, itemId) | ||||
|           res.notification = await database.getNotification($currentInstance, itemId) | ||||
|         } else { | ||||
|           res.status = await getStatusFromDatabase($currentInstance, itemId) | ||||
|           res.status = await database.getStatus($currentInstance, itemId) | ||||
|         } | ||||
|         return res | ||||
|       }, | ||||
|  |  | |||
|  | @ -12,7 +12,8 @@ export async function setAccount (instanceName, account) { | |||
|   return setGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, cloneForStorage(account)) | ||||
| } | ||||
| 
 | ||||
| export async function searchAccountsByUsername (instanceName, usernamePrefix, limit = 20) { | ||||
| export async function searchAccountsByUsername (instanceName, usernamePrefix, limit) { | ||||
|   limit = limit || 20 | ||||
|   const db = await getDatabase(instanceName) | ||||
|   return dbPromise(db, ACCOUNTS_STORE, 'readonly', (accountsStore, callback) => { | ||||
|     let keyRange = createAccountUsernamePrefixKeyRange(usernamePrefix.toLowerCase()) | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ export const notificationsCache = { | |||
| } | ||||
| 
 | ||||
| if (process.browser && process.env.NODE_ENV !== 'production') { | ||||
|   window.cacheStats = { | ||||
|   (typeof self !== 'undefined' ? self : window).cacheStats = { | ||||
|     statuses: statusesCache, | ||||
|     accounts: accountsCache, | ||||
|     relationships: relationshipsCache, | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import { dbPromise, getDatabase } from './databaseLifecycle' | ||||
| import { scheduleIdleTask } from '../_utils/scheduleIdleTask' | ||||
| import { | ||||
|   ACCOUNTS_STORE, | ||||
|   NOTIFICATION_TIMELINES_STORE, | ||||
|  | @ -12,10 +11,10 @@ import { | |||
|   TIMESTAMP | ||||
| } from './constants' | ||||
| import debounce from 'lodash-es/debounce' | ||||
| import { store } from '../_store/store' | ||||
| import { mark, stop } from '../_utils/marks' | ||||
| import { deleteAll } from './utils' | ||||
| import { createPinnedStatusKeyRange, createThreadKeyRange } from './keys' | ||||
| import { getKnownInstances } from './knownInstances' | ||||
| 
 | ||||
| const BATCH_SIZE = 20 | ||||
| const TIME_AGO = 7 * 24 * 60 * 60 * 1000 // one week ago
 | ||||
|  | @ -135,15 +134,16 @@ async function cleanup (instanceName) { | |||
| } | ||||
| 
 | ||||
| function doCleanup (instanceName) { | ||||
|   scheduleIdleTask(() => cleanup(instanceName)) | ||||
|   // run in setTimeout because we're in a worker and there's no requestIdleCallback
 | ||||
|   setTimeout(() => cleanup(instanceName)) | ||||
| } | ||||
| 
 | ||||
| function scheduledCleanup () { | ||||
| async function scheduledCleanup () { | ||||
|   console.log('scheduledCleanup') | ||||
|   let { loggedInInstancesInOrder } = store.get() | ||||
|   for (let instance of loggedInInstancesInOrder) { | ||||
|   let knownInstances = await getKnownInstances() | ||||
|   for (let instance of knownInstances) { | ||||
|     doCleanup(instance) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const scheduleCleanup = debounce(() => scheduleIdleTask(scheduledCleanup), DELAY) | ||||
| export const scheduleCleanup = debounce(scheduledCleanup, DELAY) | ||||
|  |  | |||
							
								
								
									
										3
									
								
								routes/_database/database.dev.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								routes/_database/database.dev.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| // vanilla version
 | ||||
| import * as database from './databaseWorker' | ||||
| export { database } | ||||
							
								
								
									
										7
									
								
								routes/_database/database.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								routes/_database/database.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| // workerize version
 | ||||
| let database | ||||
| if (process.browser) { | ||||
|   const worker = require('./databaseWorker') | ||||
|   database = worker() | ||||
| } | ||||
| export { database } | ||||
|  | @ -13,6 +13,7 @@ import { | |||
|   STATUS_ID, | ||||
|   USERNAME_LOWERCASE | ||||
| } from './constants' | ||||
| import { addKnownInstance, deleteKnownInstance } from './knownInstances' | ||||
| 
 | ||||
| const openReqs = {} | ||||
| const databaseCache = {} | ||||
|  | @ -86,6 +87,8 @@ export function getDatabase (instanceName) { | |||
|       } | ||||
|     } | ||||
|     req.onsuccess = () => resolve(req.result) | ||||
|   }).then(res => { | ||||
|     return addKnownInstance(instanceName).then(() => res) | ||||
|   }) | ||||
|   return databaseCache[instanceName] | ||||
| } | ||||
|  | @ -118,5 +121,5 @@ export function deleteDatabase (instanceName) { | |||
|     let req = indexedDB.deleteDatabase(instanceName) | ||||
|     req.onsuccess = () => resolve() | ||||
|     req.onerror = () => reject(req.error) | ||||
|   }) | ||||
|   }).then(() => deleteKnownInstance(instanceName)) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										187
									
								
								routes/_database/databaseWorker.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								routes/_database/databaseWorker.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,187 @@ | |||
| import { | ||||
|   getAccount as _getAccount, | ||||
|   searchAccountsByUsername as _searchAccountsByUsername, | ||||
|   setAccount as _setAccount | ||||
| } from './accounts' | ||||
| import { | ||||
|   clearDatabaseForInstance as _clearDatabaseForInstance | ||||
| } from './clear' | ||||
| import { | ||||
|   getNotificationIdsForStatuses as _getNotificationIdsForStatuses, | ||||
|   getReblogsForStatus as _getReblogsForStatus | ||||
| } from './timelines/lookup' | ||||
| import { | ||||
|   getPinnedStatuses as _getPinnedStatuses, | ||||
|   insertPinnedStatuses as _insertPinnedStatuses | ||||
| } from './timelines/pinnedStatuses' | ||||
| import { | ||||
|   getNotificationTimeline as _getNotificationTimeline, | ||||
|   getStatusThread as _getStatusThread, | ||||
|   getStatusTimeline as _getStatusTimeline, | ||||
|   getTimeline as _getTimeline | ||||
| } from './timelines/pagination' | ||||
| import { | ||||
|   getNotification as _getNotification, | ||||
|   getStatus as _getStatus | ||||
| } from './timelines/getStatusOrNotification' | ||||
| import { | ||||
|   setStatusFavorited as _setStatusFavorited, | ||||
|   setStatusMuted as _setStatusMuted, | ||||
|   setStatusPinned as _setStatusPinned, | ||||
|   setStatusReblogged as _setStatusReblogged | ||||
| } from './timelines/updateStatus' | ||||
| import { | ||||
|   deleteStatusesAndNotifications as _deleteStatusesAndNotifications | ||||
| } from './timelines/deletion' | ||||
| import { | ||||
|   insertStatusThread as _insertStatusThread, | ||||
|   insertTimelineItems as _insertTimelineItems, | ||||
|   insertTimelineNotifications as _insertTimelineNotifications, | ||||
|   insertTimelineStatuses as _insertTimelineStatuses | ||||
| } from './timelines/insertion' | ||||
| import { | ||||
|   getCustomEmoji as _getCustomEmoji, | ||||
|   getInstanceInfo as _getInstanceInfo, | ||||
|   getInstanceVerifyCredentials as _getInstanceVerifyCredentials, | ||||
|   getLists as _getLists, | ||||
|   setCustomEmoji as _setCustomEmoji, | ||||
|   setInstanceInfo as _setInstanceInfo, | ||||
|   setInstanceVerifyCredentials as _setInstanceVerifyCredentials, | ||||
|   setLists as _setLists | ||||
| } from './meta' | ||||
| import { | ||||
|   getRelationship as _getRelationship, | ||||
|   setRelationship as _setRelationship | ||||
| } from './relationships' | ||||
| 
 | ||||
| export async function getAccount (instanceName, accountId) { | ||||
|   return _getAccount(instanceName, accountId) | ||||
| } | ||||
| 
 | ||||
| export async function setAccount (instanceName, account) { | ||||
|   return _setAccount(instanceName, account) | ||||
| } | ||||
| 
 | ||||
| export async function searchAccountsByUsername (instanceName, usernamePrefix, limit) { | ||||
|   return _searchAccountsByUsername(instanceName, usernamePrefix, limit) | ||||
| } | ||||
| 
 | ||||
| export async function clearDatabaseForInstance (instanceName) { | ||||
|   return _clearDatabaseForInstance(instanceName) | ||||
| } | ||||
| 
 | ||||
| export async function getReblogsForStatus (instanceName, id) { | ||||
|   return _getReblogsForStatus(instanceName, id) | ||||
| } | ||||
| 
 | ||||
| export async function getNotificationIdsForStatuses (instanceName, statusIds) { | ||||
|   return _getNotificationIdsForStatuses(instanceName, statusIds) | ||||
| } | ||||
| 
 | ||||
| export async function insertPinnedStatuses (instanceName, accountId, statuses) { | ||||
|   return _insertPinnedStatuses(instanceName, accountId, statuses) | ||||
| } | ||||
| 
 | ||||
| export async function getPinnedStatuses (instanceName, accountId) { | ||||
|   return _getPinnedStatuses(instanceName, accountId) | ||||
| } | ||||
| 
 | ||||
| export async function getNotificationTimeline (instanceName, timeline, maxId, limit) { | ||||
|   return _getNotificationTimeline(instanceName, timeline, maxId, limit) | ||||
| } | ||||
| 
 | ||||
| export async function getStatusTimeline (instanceName, timeline, maxId, limit) { | ||||
|   return _getStatusTimeline(instanceName, timeline, maxId, limit) | ||||
| } | ||||
| 
 | ||||
| export async function getStatusThread (instanceName, statusId) { | ||||
|   return _getStatusThread(instanceName, statusId) | ||||
| } | ||||
| 
 | ||||
| export async function getTimeline (instanceName, timeline, maxId, limit) { | ||||
|   return _getTimeline(instanceName, timeline, maxId, limit) | ||||
| } | ||||
| 
 | ||||
| export async function getStatus (instanceName, id) { | ||||
|   return _getStatus(instanceName, id) | ||||
| } | ||||
| 
 | ||||
| export async function getNotification (instanceName, id) { | ||||
|   return _getNotification(instanceName, id) | ||||
| } | ||||
| 
 | ||||
| export async function setStatusFavorited (instanceName, statusId, favorited) { | ||||
|   return _setStatusFavorited(instanceName, statusId, favorited) | ||||
| } | ||||
| 
 | ||||
| export async function setStatusReblogged (instanceName, statusId, reblogged) { | ||||
|   return _setStatusReblogged(instanceName, statusId, reblogged) | ||||
| } | ||||
| 
 | ||||
| export async function setStatusPinned (instanceName, statusId, pinned) { | ||||
|   return _setStatusPinned(instanceName, statusId, pinned) | ||||
| } | ||||
| 
 | ||||
| export async function setStatusMuted (instanceName, statusId, muted) { | ||||
|   return _setStatusMuted(instanceName, statusId, muted) | ||||
| } | ||||
| 
 | ||||
| export async function deleteStatusesAndNotifications (instanceName, statusIds, notificationIds) { | ||||
|   return _deleteStatusesAndNotifications(instanceName, statusIds, notificationIds) | ||||
| } | ||||
| 
 | ||||
| export async function insertTimelineNotifications (instanceName, timeline, notifications) { | ||||
|   return _insertTimelineNotifications(instanceName, timeline, notifications) | ||||
| } | ||||
| 
 | ||||
| export async function insertTimelineStatuses (instanceName, timeline, statuses) { | ||||
|   return _insertTimelineStatuses(instanceName, timeline, statuses) | ||||
| } | ||||
| 
 | ||||
| export async function insertStatusThread (instanceName, statusId, statuses) { | ||||
|   return _insertStatusThread(instanceName, statusId, statuses) | ||||
| } | ||||
| 
 | ||||
| export async function insertTimelineItems (instanceName, timeline, timelineItems) { | ||||
|   return _insertTimelineItems(instanceName, timeline, timelineItems) | ||||
| } | ||||
| 
 | ||||
| export async function getInstanceVerifyCredentials (instanceName) { | ||||
|   return _getInstanceVerifyCredentials(instanceName) | ||||
| } | ||||
| 
 | ||||
| export async function setInstanceVerifyCredentials (instanceName, value) { | ||||
|   return _setInstanceVerifyCredentials(instanceName, value) | ||||
| } | ||||
| 
 | ||||
| export async function getInstanceInfo (instanceName) { | ||||
|   return _getInstanceInfo(instanceName) | ||||
| } | ||||
| 
 | ||||
| export async function setInstanceInfo (instanceName, value) { | ||||
|   return _setInstanceInfo(instanceName, value) | ||||
| } | ||||
| 
 | ||||
| export async function getLists (instanceName) { | ||||
|   return _getLists(instanceName) | ||||
| } | ||||
| 
 | ||||
| export async function setLists (instanceName, value) { | ||||
|   return _setLists(instanceName, value) | ||||
| } | ||||
| 
 | ||||
| export async function getCustomEmoji (instanceName) { | ||||
|   return _getCustomEmoji(instanceName) | ||||
| } | ||||
| 
 | ||||
| export async function setCustomEmoji (instanceName, value) { | ||||
|   return _setCustomEmoji(instanceName, value) | ||||
| } | ||||
| 
 | ||||
| export async function getRelationship (instanceName, accountId) { | ||||
|   return _getRelationship(instanceName, accountId) | ||||
| } | ||||
| 
 | ||||
| export async function setRelationship (instanceName, relationship) { | ||||
|   return _setRelationship(instanceName, relationship) | ||||
| } | ||||
							
								
								
									
										17
									
								
								routes/_database/knownInstances.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								routes/_database/knownInstances.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| import { set, keys, del } from 'idb-keyval' | ||||
| 
 | ||||
| const PREFIX = 'known-instance-' | ||||
| 
 | ||||
| export async function getKnownInstances () { | ||||
|   return (await keys()) | ||||
|     .filter(_ => _.startsWith(PREFIX)) | ||||
|     .map(_ => _.substring(PREFIX.length)) | ||||
| } | ||||
| 
 | ||||
| export async function addKnownInstance (instanceName) { | ||||
|   return set(PREFIX + instanceName, true) | ||||
| } | ||||
| 
 | ||||
| export async function deleteKnownInstance (instanceName) { | ||||
|   return del(PREFIX + instanceName) | ||||
| } | ||||
|  | @ -80,7 +80,9 @@ export async function getStatusThread (instanceName, statusId) { | |||
|   }) | ||||
| } | ||||
| 
 | ||||
| export async function getTimeline (instanceName, timeline, maxId = null, limit = 20) { | ||||
| export async function getTimeline (instanceName, timeline, maxId, limit) { | ||||
|   maxId = maxId || null | ||||
|   limit = limit || 20 | ||||
|   if (timeline === 'notifications') { | ||||
|     return getNotificationTimeline(instanceName, timeline, maxId, limit) | ||||
|   } else if (timeline.startsWith('status/')) { | ||||
|  |  | |||
|  | @ -1,6 +1,4 @@ | |||
| import { | ||||
|   searchAccountsByUsername as searchAccountsByUsernameInDatabase | ||||
| } from '../../_database/accounts' | ||||
| import { database } from '../../_database/database' | ||||
| 
 | ||||
| const SEARCH_RESULTS_LIMIT = 4 | ||||
| const DATABASE_SEARCH_RESULTS_LIMIT = 30 | ||||
|  | @ -8,7 +6,7 @@ const DATABASE_SEARCH_RESULTS_LIMIT = 30 | |||
| async function searchAccounts (store, searchText) { | ||||
|   searchText = searchText.substring(1) | ||||
|   let { currentInstance } = store.get() | ||||
|   let results = await searchAccountsByUsernameInDatabase( | ||||
|   let results = await database.searchAccountsByUsername( | ||||
|     currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT) | ||||
|   return results.slice(0, SEARCH_RESULTS_LIMIT) | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| const enabled = process.browser && performance.mark && ( | ||||
|   process.env.NODE_ENV !== 'production' || | ||||
|   location.search.includes('marks=true') | ||||
|   (typeof location !== 'undefined' && location.search.includes('marks=true')) | ||||
| ) | ||||
| 
 | ||||
| const perf = process.browser && performance | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ const isDev = config.dev | |||
| 
 | ||||
| module.exports = { | ||||
|   entry: config.client.entry(), | ||||
|   // uncomment to enable HMR within workers
 | ||||
|   // output: Object.assign(config.client.output(), { globalObject: 'this' }),
 | ||||
|   output: config.client.output(), | ||||
|   resolve: { | ||||
|     extensions: ['.js', '.json', '.html'] | ||||
|  | @ -45,6 +47,12 @@ module.exports = { | |||
|           MiniCssExtractPlugin.loader, | ||||
|           'css-loader' | ||||
|         ] | ||||
|       }, | ||||
|       !isDev && { // workerize-loader makes dev mode hard (e.g. HMR)
 | ||||
|         test: /\/_database\/databaseWorker\.js$/, | ||||
|         use: [ | ||||
|           'workerize-loader' | ||||
|         ] | ||||
|       } | ||||
|     ].filter(Boolean) | ||||
|   }, | ||||
|  | @ -80,6 +88,10 @@ module.exports = { | |||
|       paths: true | ||||
|     }) | ||||
|   ].concat(isDev ? [ | ||||
|     new webpack.NormalModuleReplacementPlugin( | ||||
|       /\/_database\/database\.js$/, | ||||
|       './database.dev.js' | ||||
|     ), | ||||
|     new webpack.HotModuleReplacementPlugin({ | ||||
|       requestTimeout: 120000 | ||||
|     }) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue