* feat: (wip) authorize note access
* fix: remove multiEditorEnabled
* refactor: update SNJS + eslint
* refactor: remove privileges in favor of SNJS protections
* fix: do not close editor when editing an archived note
* chore: remove progress indicator for webpack dev server
* fix: add rel="noreferrer" to bugsnag links
* chore(deps): upgrade snjs
* chore(deps): upgrade snjs
* feat: batch manager protection + react challenge modal + eslint fix
* fix: lint errors
* fix: launch state error
* fix: challenge modal: cancel instead of dismiss when pressing escape
* feat: improve focus styles
* fix: cancel session revoking when pressing escape on confirm dialog
* fix: lint warning
* chore(deps): upgrade minor versions
* feat: make SNWebCrypto a constant
* feat: add random identifier to bugsnag reports
* fix: check onKeyUp instead of onKeyDown
* feat: implement SNJS backup file password retrieval
* chore(deps): upgrade snjs
* feat: display warning banner when using the app with no account
* fix: properly color svg button
* fix: wording
* fix: hide account warning after login + improve key storage wording
* chore(deps): upgrade stylekit
* feat: use stylekit fonts for the editor
* chore(deps): bump nokogiri from 1.10.8 to 1.11.1 (#511)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.8 to 1.11.1.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.8...v1.11.1)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* chore(deps): bump ini from 1.3.5 to 1.3.8 (#504)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* fix: rename master branch to main
* fix: add missing placeholders for submodules (#516)
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
* chore(deps): upgrade snjs, babel, typescript, reach, mobx, preact
* feat: clear protection session
* fix: use correct close icon size
* fix: hide protections paragraph when no account or passcode exist
* chore(deps): remove unused dependencies
* fix: button casing
* feat: implement SNApplication.hasProtectionSources
* chore(version): 3.6.0
* feat: enable sessions management for every build
* feat: make "Protected" flag more subtle
* fix: only match protected note title
* fix: remove inconsistencies between protected note label and date
* feat: show warning when protecting a note with no protection source
* feat: make unprotecting a note a protected action
* chore(deps): upgrade snjs
* chore(version): 3.6.0-beta01
* fix: run docker with root to fix crashing on Linux (undoes 62da387d3a) (#525)
* feat: make encrypted backups protected (#524)
Co-authored-by: Baptiste Grob <60621355+baptiste-grob@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: proletarius101 <54175165+proletarius101@users.noreply.github.com>
Co-authored-by: Darius JJ Chuck <79410894+standarius@users.noreply.github.com>
Co-authored-by: Antonella Sgarlatta <antonella@standardnotes.org>
216 lines
5.7 KiB
TypeScript
216 lines
5.7 KiB
TypeScript
import {
|
|
SNComponent,
|
|
PurePayload,
|
|
ComponentMutator,
|
|
AppDataField,
|
|
EncryptionIntent,
|
|
ApplicationService,
|
|
ApplicationEvent,
|
|
removeFromArray,
|
|
} from '@standardnotes/snjs';
|
|
/* eslint-disable camelcase */
|
|
import { WebApplication } from '@/ui_models/application';
|
|
// An interface used by the Desktop app to interact with SN
|
|
import { isDesktopApplication } from '@/utils';
|
|
import { Bridge } from './bridge';
|
|
|
|
type UpdateObserverCallback = (component: SNComponent) => void
|
|
type ComponentActivationCallback = (payload: PurePayload) => void
|
|
type ComponentActivationObserver = {
|
|
id: string;
|
|
callback: ComponentActivationCallback;
|
|
}
|
|
|
|
export class DesktopManager extends ApplicationService {
|
|
|
|
$rootScope: ng.IRootScopeService
|
|
$timeout: ng.ITimeoutService
|
|
componentActivationObservers: ComponentActivationObserver[] = []
|
|
updateObservers: {
|
|
callback: UpdateObserverCallback;
|
|
}[] = [];
|
|
|
|
isDesktop = isDesktopApplication();
|
|
|
|
dataLoaded = false
|
|
lastSearchedText?: string
|
|
private removeComponentObserver?: () => void;
|
|
|
|
constructor(
|
|
$rootScope: ng.IRootScopeService,
|
|
$timeout: ng.ITimeoutService,
|
|
application: WebApplication,
|
|
private bridge: Bridge,
|
|
) {
|
|
super(application);
|
|
this.$rootScope = $rootScope;
|
|
this.$timeout = $timeout;
|
|
}
|
|
|
|
get webApplication() {
|
|
return this.application as WebApplication;
|
|
}
|
|
|
|
deinit() {
|
|
this.componentActivationObservers.length = 0;
|
|
this.updateObservers.length = 0;
|
|
this.removeComponentObserver?.();
|
|
this.removeComponentObserver = undefined;
|
|
super.deinit();
|
|
}
|
|
|
|
async onAppEvent(eventName: ApplicationEvent) {
|
|
super.onAppEvent(eventName);
|
|
if (eventName === ApplicationEvent.LocalDataLoaded) {
|
|
this.dataLoaded = true;
|
|
this.bridge.onInitialDataLoad();
|
|
} else if (eventName === ApplicationEvent.MajorDataChange) {
|
|
this.bridge.onMajorDataChange();
|
|
}
|
|
}
|
|
|
|
saveBackup() {
|
|
this.bridge.onMajorDataChange();
|
|
}
|
|
|
|
getExtServerHost() {
|
|
console.assert(
|
|
!!this.bridge.extensionsServerHost,
|
|
'extServerHost is null'
|
|
);
|
|
return this.bridge.extensionsServerHost;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
convertComponentForTransmission(component: SNComponent) {
|
|
return this.application!.protocolService!.payloadByEncryptingPayload(
|
|
component.payloadRepresentation(),
|
|
EncryptionIntent.FileDecrypted
|
|
);
|
|
}
|
|
|
|
// All `components` should be installed
|
|
syncComponentsInstallation(components: SNComponent[]) {
|
|
if (!this.isDesktop) {
|
|
return;
|
|
}
|
|
Promise.all(components.map((component) => {
|
|
return this.convertComponentForTransmission(component);
|
|
})).then((payloads) => {
|
|
this.bridge.syncComponents(
|
|
payloads.filter(payload =>
|
|
!payload.errorDecrypting && !payload.waitingForKey
|
|
)
|
|
);
|
|
});
|
|
}
|
|
|
|
registerUpdateObserver(callback: UpdateObserverCallback) {
|
|
const observer = {
|
|
callback: callback
|
|
};
|
|
this.updateObservers.push(observer);
|
|
return () => {
|
|
removeFromArray(this.updateObservers, observer);
|
|
};
|
|
}
|
|
|
|
searchText(text?: string) {
|
|
if (!this.isDesktop) {
|
|
return;
|
|
}
|
|
this.lastSearchedText = text;
|
|
this.bridge.onSearch(text);
|
|
}
|
|
|
|
redoSearch() {
|
|
if (this.lastSearchedText) {
|
|
this.searchText(this.lastSearchedText);
|
|
}
|
|
}
|
|
|
|
desktop_windowGainedFocus() {
|
|
this.$rootScope.$broadcast('window-gained-focus');
|
|
}
|
|
|
|
desktop_windowLostFocus() {
|
|
this.$rootScope.$broadcast('window-lost-focus');
|
|
}
|
|
|
|
async desktop_onComponentInstallationComplete(
|
|
componentData: any,
|
|
error: any
|
|
) {
|
|
const component = this.application!.findItem(componentData.uuid);
|
|
if (!component) {
|
|
return;
|
|
}
|
|
const updatedComponent = await this.application!.changeAndSaveItem(
|
|
component.uuid,
|
|
(m) => {
|
|
const mutator = m as ComponentMutator;
|
|
if (error) {
|
|
mutator.setAppDataItem(
|
|
AppDataField.ComponentInstallError,
|
|
error
|
|
);
|
|
} else {
|
|
mutator.local_url = componentData.content.local_url;
|
|
mutator.package_info = componentData.content.package_info;
|
|
mutator.setAppDataItem(
|
|
AppDataField.ComponentInstallError,
|
|
undefined
|
|
);
|
|
}
|
|
});
|
|
|
|
this.$timeout(() => {
|
|
for (const observer of this.updateObservers) {
|
|
observer.callback(updatedComponent as SNComponent);
|
|
}
|
|
});
|
|
}
|
|
|
|
desktop_registerComponentActivationObserver(callback: ComponentActivationCallback) {
|
|
const observer = { id: `${Math.random}`, callback: callback };
|
|
this.componentActivationObservers.push(observer);
|
|
return observer;
|
|
}
|
|
|
|
desktop_deregisterComponentActivationObserver(observer: ComponentActivationObserver) {
|
|
removeFromArray(this.componentActivationObservers, observer);
|
|
}
|
|
|
|
/* Notify observers that a component has been registered/activated */
|
|
async notifyComponentActivation(component: SNComponent) {
|
|
const serializedComponent = await this.convertComponentForTransmission(
|
|
component
|
|
);
|
|
this.$timeout(() => {
|
|
for (const observer of this.componentActivationObservers) {
|
|
observer.callback(serializedComponent);
|
|
}
|
|
});
|
|
}
|
|
|
|
async desktop_requestBackupFile() {
|
|
const data = await this.application!.createBackupFile(
|
|
EncryptionIntent.FileEncrypted
|
|
);
|
|
if (data) {
|
|
return JSON.stringify(data, null, 2);
|
|
}
|
|
}
|
|
|
|
desktop_didBeginBackup() {
|
|
this.webApplication.getAppState().beganBackupDownload();
|
|
}
|
|
|
|
desktop_didFinishBackup(success: boolean) {
|
|
this.webApplication.getAppState().endedBackupDownload(success);
|
|
}
|
|
}
|