feat: improve initial load performance on mobile (#2126)

This commit is contained in:
Mo
2023-01-03 14:15:45 -06:00
committed by GitHub
parent a447fa1ad7
commit 3c332a35f6
59 changed files with 868 additions and 3003 deletions

View File

@@ -21,6 +21,8 @@ import {
DecryptedItem,
EditorIdentifier,
FeatureIdentifier,
Environment,
ApplicationOptionsDefaults,
} from '@standardnotes/snjs'
import { makeObservable, observable } from 'mobx'
import { PanelResizedData } from '@/Types/PanelResizedData'
@@ -75,6 +77,8 @@ export class WebApplication extends SNApplication implements WebApplicationInter
defaultHost: defaultSyncServerHost,
appVersion: deviceInterface.appVersion,
webSocketUrl: webSocketUrl,
loadBatchSize:
deviceInterface.environment === Environment.Mobile ? 100 : ApplicationOptionsDefaults.loadBatchSize,
})
makeObservable(this, {

View File

@@ -140,6 +140,39 @@ export class Database {
})
}
/**
* This function is actually unused, but implemented to conform to protocol in case it is eventually needed.
* We could remove implementation and throw instead, but it might be better to offer a functional alternative instead.
*/
public async getPayloadsForKeys(keys: string[]): Promise<any[]> {
const db = (await this.openDatabase()) as IDBDatabase
return new Promise((resolve) => {
const objectStore = db.transaction(STORE_NAME).objectStore(STORE_NAME)
const payloads: any = []
let numComplete = 0
for (const key of keys) {
const getRequest = objectStore.get(key)
getRequest.onsuccess = (event) => {
const target = event.target as any
const result = target.result
if (result) {
payloads.push(result)
}
numComplete++
if (numComplete === keys.length) {
resolve(payloads)
}
}
getRequest.onerror = () => {
numComplete++
if (numComplete === keys.length) {
resolve(payloads)
}
}
}
})
}
public async getAllKeys(): Promise<string[]> {
const db = (await this.openDatabase()) as IDBDatabase

View File

@@ -2,13 +2,16 @@ import {
SNApplication,
ApplicationIdentifier,
Environment,
LegacyRawKeychainValue,
RawKeychainValue,
TransferPayload,
NamespacedRootKeyInKeychain,
extendArray,
WebOrDesktopDeviceInterface,
Platform,
FullyFormedTransferPayload,
DatabaseLoadOptions,
GetSortedPayloadsByPriority,
DatabaseFullEntryLoadChunk,
DatabaseFullEntryLoadChunkResponse,
} from '@standardnotes/snjs'
import { Database } from '../Database'
@@ -72,17 +75,6 @@ export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface
return result
}
async getAllRawStorageKeyValues() {
const results = []
for (const key of Object.keys(localStorage)) {
results.push({
key: key,
value: localStorage[key],
})
}
return results
}
async setRawStorageValue(key: string, value: string) {
localStorage.setItem(key, value)
}
@@ -111,23 +103,63 @@ export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface
}) as Promise<{ isNewDatabase?: boolean } | undefined>
}
async getAllRawDatabasePayloads(identifier: ApplicationIdentifier) {
async getDatabaseLoadChunks(
options: DatabaseLoadOptions,
identifier: string,
): Promise<DatabaseFullEntryLoadChunkResponse> {
const entries = await this.getAllDatabaseEntries(identifier)
const sorted = GetSortedPayloadsByPriority(entries, options)
const itemsKeysChunk: DatabaseFullEntryLoadChunk = {
entries: sorted.itemsKeyPayloads,
}
const contentTypePriorityChunk: DatabaseFullEntryLoadChunk = {
entries: sorted.contentTypePriorityPayloads,
}
const remainingPayloadsChunks: DatabaseFullEntryLoadChunk[] = []
for (let i = 0; i < sorted.remainingPayloads.length; i += options.batchSize) {
remainingPayloadsChunks.push({
entries: sorted.remainingPayloads.slice(i, i + options.batchSize),
})
}
const result: DatabaseFullEntryLoadChunkResponse = {
fullEntries: {
itemsKeys: itemsKeysChunk,
remainingChunks: [contentTypePriorityChunk, ...remainingPayloadsChunks],
},
remainingChunksItemCount: sorted.contentTypePriorityPayloads.length + sorted.remainingPayloads.length,
}
return result
}
async getAllDatabaseEntries(identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).getAllPayloads()
}
async saveRawDatabasePayload(payload: TransferPayload, identifier: ApplicationIdentifier) {
getDatabaseEntries<T extends FullyFormedTransferPayload = FullyFormedTransferPayload>(
identifier: string,
keys: string[],
): Promise<T[]> {
return this.databaseForIdentifier(identifier).getPayloadsForKeys(keys)
}
async saveDatabaseEntry(payload: TransferPayload, identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).savePayload(payload)
}
async saveRawDatabasePayloads(payloads: TransferPayload[], identifier: ApplicationIdentifier) {
async saveDatabaseEntries(payloads: TransferPayload[], identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).savePayloads(payloads)
}
async removeRawDatabasePayloadWithId(id: string, identifier: ApplicationIdentifier) {
async removeDatabaseEntry(id: string, identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).deletePayload(id)
}
async removeAllRawDatabasePayloads(identifier: ApplicationIdentifier) {
async removeAllDatabaseEntries(identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).clearAllPayloads()
}
@@ -141,16 +173,6 @@ export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface
return keychain[identifier]
}
async getDatabaseKeys(): Promise<string[]> {
const keys: string[] = []
for (const database of this.databases) {
extendArray(keys, await database.getAllKeys())
}
return keys
}
async setNamespacedKeychainValue(value: NamespacedRootKeyInKeychain, identifier: ApplicationIdentifier) {
let keychain = await this.getKeychainValue()
@@ -186,10 +208,6 @@ export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface
}
}
setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
return this.setKeychainValue(value)
}
abstract getKeychainValue(): Promise<RawKeychainValue>
abstract setKeychainValue(value: unknown): Promise<void>