fix: desktop interop
This commit is contained in:
@@ -1,13 +1,24 @@
|
|||||||
|
import { PurePayload, Environment } from "snjs";
|
||||||
|
|
||||||
/** Platform-specific (i-e Electron/browser) behavior is handled by a Bridge object. */
|
/** Platform-specific (i-e Electron/browser) behavior is handled by a Bridge object. */
|
||||||
export interface Bridge {
|
export interface Bridge {
|
||||||
|
environment: Environment,
|
||||||
|
|
||||||
getKeychainValue(): Promise<unknown>;
|
getKeychainValue(): Promise<unknown>;
|
||||||
setKeychainValue(value: any): Promise<void>;
|
setKeychainValue(value: any): Promise<void>;
|
||||||
clearKeychainValue(): Promise<void>;
|
clearKeychainValue(): Promise<void>;
|
||||||
|
|
||||||
|
extensionsServerHost?: string;
|
||||||
|
syncComponents(payloads: PurePayload[]): void;
|
||||||
|
onMajorDataChange(): void;
|
||||||
|
onInitialDataLoad(): void;
|
||||||
|
onSearch(text?: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KEYCHAIN_STORAGE_KEY = 'keychain';
|
const KEYCHAIN_STORAGE_KEY = 'keychain';
|
||||||
|
|
||||||
export class BrowserBridge implements Bridge {
|
export class BrowserBridge implements Bridge {
|
||||||
|
environment = Environment.Web;
|
||||||
|
|
||||||
async getKeychainValue(): Promise<unknown> {
|
async getKeychainValue(): Promise<unknown> {
|
||||||
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY);
|
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY);
|
||||||
@@ -23,4 +34,15 @@ export class BrowserBridge implements Bridge {
|
|||||||
async clearKeychainValue(): Promise<void> {
|
async clearKeychainValue(): Promise<void> {
|
||||||
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
|
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** No-ops */
|
||||||
|
|
||||||
|
syncComponents() {
|
||||||
|
}
|
||||||
|
onMajorDataChange() {
|
||||||
|
}
|
||||||
|
onInitialDataLoad() {
|
||||||
|
}
|
||||||
|
onSearch() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { SNComponent, PurePayload, ComponentMutator, AppDataField } from 'snjs';
|
import { SNComponent, PurePayload, ComponentMutator, AppDataField, ContentType } from 'snjs';
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
// An interface used by the Desktop app to interact with SN
|
// An interface used by the Desktop app to interact with SN
|
||||||
import { isDesktopApplication } from '@/utils';
|
import { isDesktopApplication } from '@/utils';
|
||||||
import { EncryptionIntent, ApplicationService, ApplicationEvent, removeFromArray } from 'snjs';
|
import { EncryptionIntent, ApplicationService, ApplicationEvent, removeFromArray } from 'snjs';
|
||||||
|
import { Bridge } from './bridge';
|
||||||
|
|
||||||
type UpdateObserverCallback = (component: SNComponent) => void
|
type UpdateObserverCallback = (component: SNComponent) => void
|
||||||
type ComponentActivationCallback = (payload: PurePayload) => void
|
type ComponentActivationCallback = (payload: PurePayload) => void
|
||||||
@@ -23,18 +24,14 @@ export class DesktopManager extends ApplicationService {
|
|||||||
isDesktop = isDesktopApplication();
|
isDesktop = isDesktopApplication();
|
||||||
|
|
||||||
dataLoaded = false
|
dataLoaded = false
|
||||||
dataLoadHandler?: () => void
|
|
||||||
majorDataChangeHandler?: () => void
|
|
||||||
extServerHost?: string
|
|
||||||
installationSyncHandler?: (payloads: PurePayload[]) => void
|
|
||||||
installComponentHandler?: (payload: PurePayload) => void
|
|
||||||
lastSearchedText?: string
|
lastSearchedText?: string
|
||||||
searchHandler?: (text?: string) => void
|
private removeComponentObserver?: () => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
$rootScope: ng.IRootScopeService,
|
$rootScope: ng.IRootScopeService,
|
||||||
$timeout: ng.ITimeoutService,
|
$timeout: ng.ITimeoutService,
|
||||||
application: WebApplication
|
application: WebApplication,
|
||||||
|
private bridge: Bridge,
|
||||||
) {
|
) {
|
||||||
super(application);
|
super(application);
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
@@ -48,6 +45,8 @@ export class DesktopManager extends ApplicationService {
|
|||||||
deinit() {
|
deinit() {
|
||||||
this.componentActivationObservers.length = 0;
|
this.componentActivationObservers.length = 0;
|
||||||
this.updateObservers.length = 0;
|
this.updateObservers.length = 0;
|
||||||
|
this.removeComponentObserver?.();
|
||||||
|
this.removeComponentObserver = undefined;
|
||||||
super.deinit();
|
super.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,33 +54,29 @@ export class DesktopManager extends ApplicationService {
|
|||||||
super.onAppEvent(eventName);
|
super.onAppEvent(eventName);
|
||||||
if (eventName === ApplicationEvent.LocalDataLoaded) {
|
if (eventName === ApplicationEvent.LocalDataLoaded) {
|
||||||
this.dataLoaded = true;
|
this.dataLoaded = true;
|
||||||
if (this.dataLoadHandler) {
|
this.bridge.onInitialDataLoad();
|
||||||
this.dataLoadHandler();
|
|
||||||
}
|
|
||||||
} else if (eventName === ApplicationEvent.MajorDataChange) {
|
} else if (eventName === ApplicationEvent.MajorDataChange) {
|
||||||
if (this.majorDataChangeHandler) {
|
this.bridge.onMajorDataChange();
|
||||||
this.majorDataChangeHandler();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveBackup() {
|
saveBackup() {
|
||||||
this.majorDataChangeHandler && this.majorDataChangeHandler();
|
this.bridge.onMajorDataChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
getExtServerHost() {
|
getExtServerHost() {
|
||||||
console.assert(
|
console.assert(
|
||||||
this.extServerHost,
|
this.bridge.extensionsServerHost,
|
||||||
'extServerHost is null'
|
'extServerHost is null'
|
||||||
);
|
);
|
||||||
return this.extServerHost;
|
return this.bridge.extensionsServerHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sending a component in its raw state is really slow for the desktop app
|
* Sending a component in its raw state is really slow for the desktop app
|
||||||
* Keys are not passed into ItemParams, so the result is not encrypted
|
* Keys are not passed into ItemParams, so the result is not encrypted
|
||||||
*/
|
*/
|
||||||
async convertComponentForTransmission(component: SNComponent) {
|
convertComponentForTransmission(component: SNComponent) {
|
||||||
return this.application!.protocolService!.payloadByEncryptingPayload(
|
return this.application!.protocolService!.payloadByEncryptingPayload(
|
||||||
component.payloadRepresentation(),
|
component.payloadRepresentation(),
|
||||||
EncryptionIntent.FileDecrypted
|
EncryptionIntent.FileDecrypted
|
||||||
@@ -96,16 +91,14 @@ export class DesktopManager extends ApplicationService {
|
|||||||
Promise.all(components.map((component) => {
|
Promise.all(components.map((component) => {
|
||||||
return this.convertComponentForTransmission(component);
|
return this.convertComponentForTransmission(component);
|
||||||
})).then((payloads) => {
|
})).then((payloads) => {
|
||||||
this.installationSyncHandler!(payloads);
|
this.bridge.syncComponents(
|
||||||
|
payloads.filter(payload =>
|
||||||
|
!payload.errorDecrypting && !payload.waitingForKey
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async installComponent(component: SNComponent) {
|
|
||||||
this.installComponentHandler!(
|
|
||||||
await this.convertComponentForTransmission(component)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerUpdateObserver(callback: UpdateObserverCallback) {
|
registerUpdateObserver(callback: UpdateObserverCallback) {
|
||||||
const observer = {
|
const observer = {
|
||||||
callback: callback
|
callback: callback
|
||||||
@@ -121,7 +114,7 @@ export class DesktopManager extends ApplicationService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.lastSearchedText = text;
|
this.lastSearchedText = text;
|
||||||
this.searchHandler && this.searchHandler(text);
|
this.bridge.onSearch(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
redoSearch() {
|
redoSearch() {
|
||||||
@@ -130,11 +123,6 @@ export class DesktopManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass null to cancel search
|
|
||||||
desktop_setSearchHandler(handler: (text?: string) => void) {
|
|
||||||
this.searchHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
desktop_windowGainedFocus() {
|
desktop_windowGainedFocus() {
|
||||||
this.$rootScope.$broadcast('window-gained-focus');
|
this.$rootScope.$broadcast('window-gained-focus');
|
||||||
}
|
}
|
||||||
@@ -199,38 +187,16 @@ export class DesktopManager extends ApplicationService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used to resolve 'sn://' */
|
onExtensionsReady() {
|
||||||
desktop_setExtServerHost(host: string) {
|
|
||||||
this.extServerHost = host;
|
|
||||||
this.webApplication.getAppState().desktopExtensionsReady();
|
this.webApplication.getAppState().desktopExtensionsReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_setComponentInstallationSyncHandler(handler: (payloads: PurePayload[]) => void) {
|
desktop_requestBackupFile() {
|
||||||
this.installationSyncHandler = handler;
|
return this.application!.createBackupFile(
|
||||||
}
|
|
||||||
|
|
||||||
desktop_setInstallComponentHandler(handler: (payload: PurePayload) => void) {
|
|
||||||
this.installComponentHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
desktop_setInitialDataLoadHandler(handler: () => void) {
|
|
||||||
this.dataLoadHandler = handler;
|
|
||||||
if (this.dataLoaded) {
|
|
||||||
this.dataLoadHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async desktop_requestBackupFile(callback: (data: any) => void) {
|
|
||||||
const data = await this.application!.createBackupFile(
|
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
callback(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
desktop_setMajorDataChangeHandler(handler: () => void) {
|
|
||||||
this.majorDataChangeHandler = handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop_didBeginBackup() {
|
desktop_didBeginBackup() {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export class WebApplication extends SNApplication {
|
|||||||
bridge
|
bridge
|
||||||
);
|
);
|
||||||
super(
|
super(
|
||||||
Environment.Web,
|
bridge.environment,
|
||||||
platformFromString(getPlatformString()),
|
platformFromString(getPlatformString()),
|
||||||
deviceInterface,
|
deviceInterface,
|
||||||
new SNWebCrypto(),
|
new SNWebCrypto(),
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ export class ApplicationGroup {
|
|||||||
const desktopService = new DesktopManager(
|
const desktopService = new DesktopManager(
|
||||||
this.$rootScope,
|
this.$rootScope,
|
||||||
this.$timeout,
|
this.$timeout,
|
||||||
application
|
application,
|
||||||
|
this.bridge,
|
||||||
);
|
);
|
||||||
const keyboardService = new KeyboardManager();
|
const keyboardService = new KeyboardManager();
|
||||||
const lockService = new LockManager(
|
const lockService = new LockManager(
|
||||||
|
|||||||
Reference in New Issue
Block a user