refactor: improve device interface types (#996)

This commit is contained in:
Mo
2022-04-22 13:54:34 -05:00
committed by GitHub
parent 68ad0f17ae
commit abb85b3f11
22 changed files with 296 additions and 235 deletions

View File

@@ -0,0 +1,11 @@
import { DeviceInterface, Environment } from '@standardnotes/snjs'
import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice'
import { WebCommunicationReceiver } from './DesktopWebCommunication'
export function isDesktopDevice(x: DeviceInterface): x is DesktopDeviceInterface {
return x.environment === Environment.Desktop
}
export interface DesktopDeviceInterface extends WebOrDesktopDevice, WebCommunicationReceiver {
environment: Environment.Desktop
}

View File

@@ -0,0 +1 @@
export { Environment, RawKeychainValue } from '@standardnotes/snjs'

View File

@@ -0,0 +1,44 @@
import { DecryptedTransferPayload } from '@standardnotes/snjs'
/** Receives communications emitted by Web Core. This would be the Desktop client. */
export interface WebCommunicationReceiver {
localBackupsCount(): Promise<number>
viewlocalBackups(): void
deleteLocalBackups(): Promise<void>
syncComponents(payloads: unknown[]): void
onMajorDataChange(): void
onInitialDataLoad(): void
onSignOut(): void
onSearch(text?: string): void
downloadBackup(): void | Promise<void>
get extensionsServerHost(): string
}
/** Receives communications emitted by the desktop client. This would be Web Core. */
export interface DesktopCommunicationReceiver {
updateAvailable(): void
windowGainedFocus(): void
windowLostFocus(): void
onComponentInstallationComplete(
componentData: DecryptedTransferPayload,
error: unknown,
): Promise<void>
requestBackupFile(): Promise<string | undefined>
didBeginBackup(): void
didFinishBackup(success: boolean): void
}

View File

@@ -0,0 +1,8 @@
import { WebOrDesktopDevice } from './WebOrDesktopDevice'
export type StartApplication = (
defaultSyncServerHost: string,
device: WebOrDesktopDevice,
enableUnfinishedFeatures: boolean,
webSocketUrl: string,
) => Promise<void>

View File

@@ -0,0 +1,26 @@
import { Environment, RawKeychainValue } from '@standardnotes/snjs'
import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice'
const KEYCHAIN_STORAGE_KEY = 'keychain'
export class WebDevice extends WebOrDesktopDevice {
environment = Environment.Web
async getKeychainValue(): Promise<RawKeychainValue> {
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY)
if (value) {
return JSON.parse(value)
}
return {}
}
async setKeychainValue(value: RawKeychainValue): Promise<void> {
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value))
}
async clearRawKeychainValue(): Promise<void> {
localStorage.removeItem(KEYCHAIN_STORAGE_KEY)
}
}

View File

@@ -0,0 +1,176 @@
import {
SNApplication,
ApplicationIdentifier,
Environment,
LegacyRawKeychainValue,
RawKeychainValue,
TransferPayload,
NamespacedRootKeyInKeychain,
} from '@standardnotes/snjs'
import { Database } from '../Database'
import { WebOrDesktopDeviceInterface } from './WebOrDesktopDeviceInterface'
export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface {
constructor(public appVersion: string) {}
private databases: Database[] = []
abstract environment: Environment
setApplication(application: SNApplication) {
const database = new Database(application.identifier, application.alertService)
this.databases.push(database)
}
public async getJsonParsedRawStorageValue(key: string): Promise<unknown | undefined> {
const value = await this.getRawStorageValue(key)
if (value == undefined) {
return undefined
}
try {
return JSON.parse(value)
} catch (e) {
return value
}
}
private databaseForIdentifier(identifier: ApplicationIdentifier) {
return this.databases.find((database) => database.databaseName === identifier) as Database
}
deinit() {
for (const database of this.databases) {
database.deinit()
}
this.databases = []
}
async getRawStorageValue(key: string): Promise<string | undefined> {
const result = localStorage.getItem(key)
if (result == undefined) {
return undefined
}
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)
}
async removeRawStorageValue(key: string) {
localStorage.removeItem(key)
}
async removeAllRawStorageValues() {
localStorage.clear()
}
async openDatabase(identifier: ApplicationIdentifier) {
this.databaseForIdentifier(identifier).unlock()
return new Promise((resolve, reject) => {
this.databaseForIdentifier(identifier)
.openDatabase(() => {
resolve({ isNewDatabase: true })
})
.then(() => {
resolve({ isNewDatabase: false })
})
.catch((error) => {
reject(error)
})
}) as Promise<{ isNewDatabase?: boolean } | undefined>
}
async getAllRawDatabasePayloads(identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).getAllPayloads()
}
async saveRawDatabasePayload(payload: TransferPayload, identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).savePayload(payload)
}
async saveRawDatabasePayloads(payloads: TransferPayload[], identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).savePayloads(payloads)
}
async removeRawDatabasePayloadWithId(id: string, identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).deletePayload(id)
}
async removeAllRawDatabasePayloads(identifier: ApplicationIdentifier) {
return this.databaseForIdentifier(identifier).clearAllPayloads()
}
async getNamespacedKeychainValue(identifier: ApplicationIdentifier) {
const keychain = await this.getKeychainValue()
if (!keychain) {
return
}
return keychain[identifier]
}
async setNamespacedKeychainValue(
value: NamespacedRootKeyInKeychain,
identifier: ApplicationIdentifier,
) {
let keychain = await this.getKeychainValue()
if (!keychain) {
keychain = {}
}
return this.setKeychainValue({
...keychain,
[identifier]: value,
})
}
async clearNamespacedKeychainValue(identifier: ApplicationIdentifier) {
const keychain = await this.getKeychainValue()
if (!keychain) {
return
}
delete keychain[identifier]
return this.setKeychainValue(keychain)
}
setRawKeychainValue(value: unknown): Promise<void> {
return this.setKeychainValue(value)
}
openUrl(url: string) {
const win = window.open(url, '_blank')
if (win) {
win.focus()
}
}
setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
return this.setKeychainValue(value)
}
abstract getKeychainValue(): Promise<RawKeychainValue>
abstract setKeychainValue(value: unknown): Promise<void>
abstract clearRawKeychainValue(): Promise<void>
}

View File

@@ -0,0 +1,9 @@
import { DeviceInterface, RawKeychainValue } from '@standardnotes/snjs'
export interface WebOrDesktopDeviceInterface extends DeviceInterface {
readonly appVersion: string
getKeychainValue(): Promise<RawKeychainValue>
setKeychainValue(value: RawKeychainValue): Promise<void>
}