Compiles
This commit is contained in:
@@ -1,48 +1,46 @@
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import { EncryptionIntents, ProtectedActions } from 'snjs';
|
||||
|
||||
|
||||
export class ArchiveManager {
|
||||
/* @ngInject */
|
||||
constructor(lockManager, authManager, modelManager, privilegesManager) {
|
||||
constructor(lockManager, application, authManager, modelManager, privilegesManager) {
|
||||
this.lockManager = lockManager;
|
||||
this.authManager = authManager;
|
||||
this.modelManager = modelManager;
|
||||
modelManager = modelManager;
|
||||
this.privilegesManager = privilegesManager;
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
/*
|
||||
Public
|
||||
*/
|
||||
|
||||
/** @public */
|
||||
async downloadBackup(encrypted) {
|
||||
return this.downloadBackupOfItems(this.modelManager.allItems, encrypted);
|
||||
return this.downloadBackupOfItems(modelManager.allItems, encrypted);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
async downloadBackupOfItems(items, encrypted) {
|
||||
const run = async () => {
|
||||
// download in Standard Notes format
|
||||
let keys, authParams;
|
||||
if(encrypted) {
|
||||
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||
keys = this.lockManager.keys();
|
||||
authParams = this.lockManager.passcodeAuthParams();
|
||||
} else {
|
||||
keys = await this.authManager.keys();
|
||||
authParams = await this.authManager.getAuthParams();
|
||||
}
|
||||
}
|
||||
this.__itemsData(items, keys, authParams).then((data) => {
|
||||
const intent = encrypted
|
||||
? EncryptionIntents.FileEncrypted
|
||||
: EncryptionIntents.FileDecrypted;
|
||||
this.itemsData(items, intent).then((data) => {
|
||||
const modifier = encrypted ? "Encrypted" : "Decrypted";
|
||||
this.__downloadData(data, `Standard Notes ${modifier} Backup - ${this.__formattedDate()}.txt`);
|
||||
|
||||
this.downloadData(
|
||||
data,
|
||||
`Standard Notes ${modifier} Backup - ${this.formattedDate()}.txt`
|
||||
);
|
||||
// download as zipped plain text files
|
||||
if(!keys) {
|
||||
this.__downloadZippedItems(items);
|
||||
if (!encrypted) {
|
||||
this.downloadZippedItems(items);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if(await this.privilegesManager.actionRequiresPrivilege(PrivilegesManager.ActionManageBackups)) {
|
||||
this.godService.presentPrivilegesModal(PrivilegesManager.ActionManageBackups, () => {
|
||||
if (await this.privilegesManager.actionRequiresPrivilege(ProtectedActions.ManageBackups)) {
|
||||
this.godService.presentPrivilegesModal(ProtectedActions.ManageBackups, () => {
|
||||
run();
|
||||
});
|
||||
} else {
|
||||
@@ -50,29 +48,30 @@ export class ArchiveManager {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Private
|
||||
*/
|
||||
|
||||
__formattedDate() {
|
||||
/** @private */
|
||||
formattedDate() {
|
||||
var string = `${new Date()}`;
|
||||
// Match up to the first parenthesis, i.e do not include '(Central Standard Time)'
|
||||
var matches = string.match(/^(.*?) \(/);
|
||||
if(matches.length >= 2) {
|
||||
if (matches.length >= 2) {
|
||||
return matches[1];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
async __itemsData(items, keys, authParams) {
|
||||
const data = await this.modelManager.getJSONDataForItems(items, keys, authParams);
|
||||
const blobData = new Blob([data], {type: 'text/json'});
|
||||
/** @private */
|
||||
async itemsData(items, intent) {
|
||||
const data = await this.application.protocolService.createBackupFile({
|
||||
subItems: items,
|
||||
intent: intent
|
||||
});
|
||||
const blobData = new Blob([data], { type: 'text/json' });
|
||||
return blobData;
|
||||
}
|
||||
|
||||
__loadZip(callback) {
|
||||
if(window.zip) {
|
||||
callback();
|
||||
/** @private */
|
||||
async loadZip() {
|
||||
if (window.zip) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,75 +80,77 @@ export class ArchiveManager {
|
||||
scriptTag.async = false;
|
||||
var headTag = document.getElementsByTagName('head')[0];
|
||||
headTag.appendChild(scriptTag);
|
||||
scriptTag.onload = function() {
|
||||
zip.workerScriptsPath = "assets/zip/";
|
||||
callback();
|
||||
};
|
||||
}
|
||||
|
||||
__downloadZippedItems(items) {
|
||||
this.__loadZip(() => {
|
||||
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
|
||||
var index = 0;
|
||||
|
||||
const nextFile = () => {
|
||||
var item = items[index];
|
||||
var name, contents;
|
||||
|
||||
if(item.content_type === "Note") {
|
||||
name = item.content.title;
|
||||
contents = item.content.text;
|
||||
} else {
|
||||
name = item.content_type;
|
||||
contents = JSON.stringify(item.content, null, 2);
|
||||
}
|
||||
|
||||
if(!name) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
const blob = new Blob([contents], {type: 'text/plain'});
|
||||
let filePrefix = name.replace(/\//g, "").replace(/\\+/g, "");
|
||||
const fileSuffix = `-${item.uuid.split("-")[0]}.txt`;
|
||||
// Standard max filename length is 255. Slice the note name down to allow filenameEnd
|
||||
filePrefix = filePrefix.slice(0, (255 - fileSuffix.length));
|
||||
const fileName = `${item.content_type}/${filePrefix}${fileSuffix}`;
|
||||
zipWriter.add(fileName, new zip.BlobReader(blob), () => {
|
||||
index++;
|
||||
if(index < items.length) {
|
||||
nextFile();
|
||||
} else {
|
||||
zipWriter.close((blob) => {
|
||||
this.__downloadData(blob, `Standard Notes Backup - ${this.__formattedDate()}.zip`);
|
||||
zipWriter = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
nextFile();
|
||||
}, onerror);
|
||||
return new Promise((resolve, reject) => {
|
||||
scriptTag.onload = function () {
|
||||
zip.workerScriptsPath = "assets/zip/";
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** @private */
|
||||
async downloadZippedItems(items) {
|
||||
await this.loadZip();
|
||||
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
|
||||
var index = 0;
|
||||
|
||||
__hrefForData(data) {
|
||||
const nextFile = () => {
|
||||
var item = items[index];
|
||||
var name, contents;
|
||||
|
||||
if (item.content_type === "Note") {
|
||||
name = item.content.title;
|
||||
contents = item.content.text;
|
||||
} else {
|
||||
name = item.content_type;
|
||||
contents = JSON.stringify(item.content, null, 2);
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
const blob = new Blob([contents], { type: 'text/plain' });
|
||||
let filePrefix = name.replace(/\//g, "").replace(/\\+/g, "");
|
||||
const fileSuffix = `-${item.uuid.split("-")[0]}.txt`;
|
||||
// Standard max filename length is 255. Slice the note name down to allow filenameEnd
|
||||
filePrefix = filePrefix.slice(0, (255 - fileSuffix.length));
|
||||
const fileName = `${item.content_type}/${filePrefix}${fileSuffix}`;
|
||||
zipWriter.add(fileName, new zip.BlobReader(blob), () => {
|
||||
index++;
|
||||
if (index < items.length) {
|
||||
nextFile();
|
||||
} else {
|
||||
zipWriter.close((blob) => {
|
||||
this.downloadData(blob, `Standard Notes Backup - ${this.formattedDate()}.zip`);
|
||||
zipWriter = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
nextFile();
|
||||
}, onerror);
|
||||
}
|
||||
|
||||
|
||||
/** @private */
|
||||
hrefForData(data) {
|
||||
// If we are replacing a previously generated file we need to
|
||||
// manually revoke the object URL to avoid memory leaks.
|
||||
if (this.textFile !== null) {
|
||||
window.URL.revokeObjectURL(this.textFile);
|
||||
}
|
||||
|
||||
this.textFile = window.URL.createObjectURL(data);
|
||||
|
||||
// returns a URL you can use as a href
|
||||
return this.textFile;
|
||||
}
|
||||
|
||||
__downloadData(data, fileName) {
|
||||
/** @private */
|
||||
downloadData(data, fileName) {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute('download', fileName);
|
||||
link.href = this.__hrefForData(data);
|
||||
link.href = this.hrefForData(data);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable camelcase */
|
||||
// An interface used by the Desktop app to interact with SN
|
||||
import _ from 'lodash';
|
||||
import pull from 'lodash/pull';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFItemParams, SFModelManager } from 'snjs';
|
||||
import { EncryptionIntents } from 'snjs';
|
||||
|
||||
const COMPONENT_DATA_KEY_INSTALL_ERROR = 'installError';
|
||||
const COMPONENT_CONTENT_KEY_PACKAGE_INFO = 'package_info';
|
||||
@@ -12,33 +13,26 @@ export class DesktopManager {
|
||||
constructor(
|
||||
$rootScope,
|
||||
$timeout,
|
||||
modelManager,
|
||||
syncManager,
|
||||
authManager,
|
||||
lockManager,
|
||||
appState
|
||||
application,
|
||||
appState,
|
||||
) {
|
||||
this.lockManager = lockManager;
|
||||
this.modelManager = modelManager;
|
||||
this.authManager = authManager;
|
||||
this.syncManager = syncManager;
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.appState = appState;
|
||||
this.timeout = $timeout;
|
||||
this.updateObservers = [];
|
||||
this.application = application;
|
||||
this.componentActivationObservers = [];
|
||||
|
||||
this.updateObservers = [];
|
||||
this.isDesktop = isDesktopApplication();
|
||||
|
||||
$rootScope.$on("initial-data-loaded", () => {
|
||||
$rootScope.$on('initial-data-loaded', () => {
|
||||
this.dataLoaded = true;
|
||||
if(this.dataLoadHandler) {
|
||||
if (this.dataLoadHandler) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on("major-data-change", () => {
|
||||
if(this.majorDataChangeHandler) {
|
||||
$rootScope.$on('major-data-change', () => {
|
||||
if (this.majorDataChangeHandler) {
|
||||
this.majorDataChangeHandler();
|
||||
}
|
||||
});
|
||||
@@ -56,17 +50,20 @@ export class DesktopManager {
|
||||
return this.extServerHost;
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
async convertComponentForTransmission(component) {
|
||||
return new SFItemParams(component).paramsForExportFile(true);
|
||||
return this.application.protocolService.payloadByEncryptingPayload({
|
||||
payload: component.payloadRepresentation(),
|
||||
intent: EncryptionIntents.FileDecrypted
|
||||
});
|
||||
}
|
||||
|
||||
// All `components` should be installed
|
||||
syncComponentsInstallation(components) {
|
||||
if(!this.isDesktop) {
|
||||
if (!this.isDesktop) {
|
||||
return;
|
||||
}
|
||||
Promise.all(components.map((component) => {
|
||||
@@ -91,21 +88,21 @@ export class DesktopManager {
|
||||
}
|
||||
|
||||
searchText(text) {
|
||||
if(!this.isDesktop) {
|
||||
if (!this.isDesktop) {
|
||||
return;
|
||||
}
|
||||
this.lastSearchedText = text;
|
||||
this.searchHandler && this.searchHandler(text);
|
||||
}
|
||||
|
||||
redoSearch() {
|
||||
if(this.lastSearchedText) {
|
||||
redoSearch() {
|
||||
if (this.lastSearchedText) {
|
||||
this.searchText(this.lastSearchedText);
|
||||
}
|
||||
}
|
||||
|
||||
deregisterUpdateObserver(observer) {
|
||||
_.pull(this.updateObservers, observer);
|
||||
pull(this.updateObservers, observer);
|
||||
}
|
||||
|
||||
// Pass null to cancel search
|
||||
@@ -114,19 +111,19 @@ export class DesktopManager {
|
||||
}
|
||||
|
||||
desktop_windowGainedFocus() {
|
||||
this.$rootScope.$broadcast("window-gained-focus");
|
||||
this.$rootScope.$broadcast('window-gained-focus');
|
||||
}
|
||||
|
||||
desktop_windowLostFocus() {
|
||||
this.$rootScope.$broadcast("window-lost-focus");
|
||||
this.$rootScope.$broadcast('window-lost-focus');
|
||||
}
|
||||
|
||||
desktop_onComponentInstallationComplete(componentData, error) {
|
||||
const component = this.modelManager.findItem(componentData.uuid);
|
||||
if(!component) {
|
||||
async desktop_onComponentInstallationComplete(componentData, error) {
|
||||
const component = await this.application.findItem({ uuid: componentData.uuid });
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
if(error) {
|
||||
if (error) {
|
||||
component.setAppDataItem(
|
||||
COMPONENT_DATA_KEY_INSTALL_ERROR,
|
||||
error
|
||||
@@ -136,35 +133,30 @@ export class DesktopManager {
|
||||
COMPONENT_CONTENT_KEY_PACKAGE_INFO,
|
||||
COMPONENT_CONTENT_KEY_LOCAL_URL
|
||||
];
|
||||
for(const key of permissableKeys) {
|
||||
for (const key of permissableKeys) {
|
||||
component[key] = componentData.content[key];
|
||||
}
|
||||
this.modelManager.notifySyncObserversOfModels(
|
||||
[component],
|
||||
SFModelManager.MappingSourceDesktopInstalled
|
||||
);
|
||||
component.setAppDataItem(
|
||||
COMPONENT_DATA_KEY_INSTALL_ERROR,
|
||||
null
|
||||
);
|
||||
}
|
||||
this.modelManager.setItemDirty(component);
|
||||
this.syncManager.sync();
|
||||
this.timeout(() => {
|
||||
for(const observer of this.updateObservers) {
|
||||
this.application.saveItem({ item: component });
|
||||
this.$timeout(() => {
|
||||
for (const observer of this.updateObservers) {
|
||||
observer.callback(component);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
desktop_registerComponentActivationObserver(callback) {
|
||||
const observer = {id: Math.random, callback: callback};
|
||||
const observer = { id: Math.random, callback: callback };
|
||||
this.componentActivationObservers.push(observer);
|
||||
return observer;
|
||||
}
|
||||
|
||||
desktop_deregisterComponentActivationObserver(observer) {
|
||||
_.pull(this.componentActivationObservers, observer);
|
||||
pull(this.componentActivationObservers, observer);
|
||||
}
|
||||
|
||||
/* Notify observers that a component has been registered/activated */
|
||||
@@ -172,14 +164,14 @@ export class DesktopManager {
|
||||
const serializedComponent = await this.convertComponentForTransmission(
|
||||
component
|
||||
);
|
||||
this.timeout(() => {
|
||||
for(const observer of this.componentActivationObservers) {
|
||||
this.$timeout(() => {
|
||||
for (const observer of this.componentActivationObservers) {
|
||||
observer.callback(serializedComponent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Used to resolve "sn://" */
|
||||
/* Used to resolve 'sn://' */
|
||||
desktop_setExtServerHost(host) {
|
||||
this.extServerHost = host;
|
||||
this.appState.desktopExtensionsReady();
|
||||
@@ -195,28 +187,16 @@ export class DesktopManager {
|
||||
|
||||
desktop_setInitialDataLoadHandler(handler) {
|
||||
this.dataLoadHandler = handler;
|
||||
if(this.dataLoaded) {
|
||||
if (this.dataLoaded) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
}
|
||||
|
||||
async desktop_requestBackupFile(callback) {
|
||||
let keys, authParams;
|
||||
if(this.authManager.offline() && this.lockManager.hasPasscode()) {
|
||||
keys = this.lockManager.keys();
|
||||
authParams = this.lockManager.passcodeAuthParams();
|
||||
} else {
|
||||
keys = await this.authManager.keys();
|
||||
authParams = await this.authManager.getAuthParams();
|
||||
}
|
||||
const nullOnEmpty = true;
|
||||
this.modelManager.getAllItemsJSONData(
|
||||
keys,
|
||||
authParams,
|
||||
nullOnEmpty
|
||||
).then((data) => {
|
||||
callback(data);
|
||||
const data = await this.application.protocolService.createBackupFile({
|
||||
returnIfEmpty: true
|
||||
});
|
||||
callback(data);
|
||||
}
|
||||
|
||||
desktop_setMajorDataChangeHandler(handler) {
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
export { ActionsManager } from './actionsManager';
|
||||
export { ArchiveManager } from './archiveManager';
|
||||
export { AuthManager } from './authManager';
|
||||
export { ComponentManager } from './componentManager';
|
||||
export { DatabaseManager } from './databaseManager';
|
||||
export { DesktopManager } from './desktopManager';
|
||||
export { HttpManager } from './httpManager';
|
||||
export { KeyboardManager } from './keyboardManager';
|
||||
export { MigrationManager } from './migrationManager';
|
||||
export { ModelManager } from './modelManager';
|
||||
export { NativeExtManager } from './nativeExtManager';
|
||||
export { LockManager } from './lockManager';
|
||||
export { PrivilegesManager } from './privilegesManager';
|
||||
export { SessionHistory } from './sessionHistory';
|
||||
export { SingletonManager } from './singletonManager';
|
||||
export { StatusManager } from './statusManager';
|
||||
export { StorageManager } from './storageManager';
|
||||
export { SyncManager } from './syncManager';
|
||||
export { ThemeManager } from './themeManager';
|
||||
export { AlertManager } from './alertManager';
|
||||
export { PreferencesManager } from './preferencesManager';
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
export class KeyboardManager {
|
||||
/** @public */
|
||||
export const KeyboardKeys = {
|
||||
Tab: "Tab",
|
||||
Backspace: "Backspace",
|
||||
Up: "ArrowUp",
|
||||
Down: "ArrowDown",
|
||||
};
|
||||
/** @public */
|
||||
export const KeyboardModifiers = {
|
||||
Shift: "Shift",
|
||||
Ctrl: "Control",
|
||||
/** ⌘ key on Mac, ⊞ key on Windows */
|
||||
Meta: "Meta",
|
||||
Alt: "Alt",
|
||||
};
|
||||
/** @private */
|
||||
const KeyboardKeyEvents = {
|
||||
Down: "KeyEventDown",
|
||||
Up: "KeyEventUp"
|
||||
};
|
||||
|
||||
export class KeyboardManager {
|
||||
constructor() {
|
||||
this.observers = [];
|
||||
|
||||
KeyboardManager.KeyTab = "Tab";
|
||||
KeyboardManager.KeyBackspace = "Backspace";
|
||||
KeyboardManager.KeyUp = "ArrowUp";
|
||||
KeyboardManager.KeyDown = "ArrowDown";
|
||||
|
||||
KeyboardManager.KeyModifierShift = "Shift";
|
||||
KeyboardManager.KeyModifierCtrl = "Control";
|
||||
// ⌘ key on Mac, ⊞ key on Windows
|
||||
KeyboardManager.KeyModifierMeta = "Meta";
|
||||
KeyboardManager.KeyModifierAlt = "Alt";
|
||||
|
||||
KeyboardManager.KeyEventDown = "KeyEventDown";
|
||||
KeyboardManager.KeyEventUp = "KeyEventUp";
|
||||
|
||||
KeyboardManager.AllModifiers = [
|
||||
KeyboardManager.KeyModifierShift,
|
||||
KeyboardManager.KeyModifierCtrl,
|
||||
KeyboardManager.KeyModifierMeta,
|
||||
KeyboardManager.KeyModifierAlt
|
||||
];
|
||||
|
||||
window.addEventListener('keydown', this.handleKeyDown.bind(this));
|
||||
window.addEventListener('keyup', this.handleKeyUp.bind(this));
|
||||
}
|
||||
|
||||
modifiersForEvent(event) {
|
||||
const eventModifiers = KeyboardManager.AllModifiers.filter((modifier) => {
|
||||
const allModifiers = Object.keys(KeyboardModifiers).map((key) => KeyboardModifiers[key]);
|
||||
const eventModifiers = allModifiers.filter((modifier) => {
|
||||
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
||||
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
||||
const matches = (
|
||||
((event.ctrlKey || event.key == KeyboardManager.KeyModifierCtrl) && modifier === KeyboardManager.KeyModifierCtrl) ||
|
||||
((event.metaKey || event.key == KeyboardManager.KeyModifierMeta) && modifier === KeyboardManager.KeyModifierMeta) ||
|
||||
((event.altKey || event.key == KeyboardManager.KeyModifierAlt) && modifier === KeyboardManager.KeyModifierAlt) ||
|
||||
((event.shiftKey || event.key == KeyboardManager.KeyModifierShift) && modifier === KeyboardManager.KeyModifierShift)
|
||||
((event.ctrlKey || event.key === KeyboardModifiers.Ctrl) && modifier === KeyboardModifiers.Ctrl) ||
|
||||
((event.metaKey || event.key === KeyboardModifiers.Meta) && modifier === KeyboardModifiers.Meta) ||
|
||||
((event.altKey || event.key === KeyboardModifiers.Alt) && modifier === KeyboardModifiers.Alt) ||
|
||||
((event.shiftKey || event.key === KeyboardModifiers.Shift) && modifier === KeyboardModifiers.Shift)
|
||||
);
|
||||
|
||||
return matches;
|
||||
@@ -45,50 +44,50 @@ export class KeyboardManager {
|
||||
return eventModifiers;
|
||||
}
|
||||
|
||||
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
||||
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
||||
const eventModifiers = this.modifiersForEvent(event);
|
||||
|
||||
if(eventModifiers.length != modifiers.length) {
|
||||
if (eventModifiers.length !== modifiers.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const modifier of modifiers) {
|
||||
if(!eventModifiers.includes(modifier)) {
|
||||
for (const modifier of modifiers) {
|
||||
if (!eventModifiers.includes(modifier)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifers match, check key
|
||||
if(!key) {
|
||||
if (!key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
||||
// In our case we don't differentiate between the two.
|
||||
return key.toLowerCase() == event.key.toLowerCase();
|
||||
return key.toLowerCase() === event.key.toLowerCase();
|
||||
}
|
||||
|
||||
notifyObserver(event, keyEventType) {
|
||||
for(const observer of this.observers) {
|
||||
if(observer.element && event.target != observer.element) {
|
||||
for (const observer of this.observers) {
|
||||
if (observer.element && event.target !== observer.element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.elements && !observer.elements.includes(event.target)) {
|
||||
if (observer.elements && !observer.elements.includes(event.target)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.notElement && observer.notElement == event.target) {
|
||||
if (observer.notElement && observer.notElement === event.target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
||||
if (observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
||||
const callback = keyEventType == KeyboardManager.KeyEventDown ? observer.onKeyDown : observer.onKeyUp;
|
||||
if(callback) {
|
||||
if (this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
||||
const callback = keyEventType === KeyboardKeyEvents.Down ? observer.onKeyDown : observer.onKeyUp;
|
||||
if (callback) {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
@@ -96,15 +95,15 @@ export class KeyboardManager {
|
||||
}
|
||||
|
||||
handleKeyDown(event) {
|
||||
this.notifyObserver(event, KeyboardManager.KeyEventDown);
|
||||
this.notifyObserver(event, KeyboardKeyEvents.Down);
|
||||
}
|
||||
|
||||
handleKeyUp(event) {
|
||||
this.notifyObserver(event, KeyboardManager.KeyEventUp);
|
||||
this.notifyObserver(event, KeyboardKeyEvents.Up);
|
||||
}
|
||||
|
||||
addKeyObserver({key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds}) {
|
||||
const observer = {key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds};
|
||||
addKeyObserver({ key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds }) {
|
||||
const observer = { key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds };
|
||||
this.observers.push(observer);
|
||||
return observer;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import {
|
||||
APP_STATE_EVENT_WINDOW_DID_BLUR,
|
||||
APP_STATE_EVENT_WINDOW_DID_FOCUS
|
||||
} from '../state';
|
||||
import { AppStateEvents } from '../state';
|
||||
|
||||
const MILLISECONDS_PER_SECOND = 1000;
|
||||
const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND;
|
||||
@@ -26,9 +22,9 @@ export class LockManager {
|
||||
|
||||
observeVisibility() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if(eventName === APP_STATE_EVENT_WINDOW_DID_BLUR) {
|
||||
if(eventName === AppStateEvents.WindowDidBlur) {
|
||||
this.documentVisibilityChanged(false);
|
||||
} else if(eventName === APP_STATE_EVENT_WINDOW_DID_FOCUS) {
|
||||
} else if(eventName === AppStateEvents.WindowDidFocus) {
|
||||
this.documentVisibilityChanged(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,183 +1,177 @@
|
||||
/* A class for handling installation of system extensions */
|
||||
import { isDesktopApplication, dictToArray } from '@/utils';
|
||||
import { SFPredicate, ContentTypes, CreateMaxPayloadFromAnyObject } from 'snjs';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { SFPredicate } from 'snjs';
|
||||
const STREAM_ITEMS_PERMISSION = 'stream-items';
|
||||
|
||||
/** A class for handling installation of system extensions */
|
||||
export class NativeExtManager {
|
||||
/* @ngInject */
|
||||
constructor(modelManager, syncManager, singletonManager) {
|
||||
this.modelManager = modelManager;
|
||||
this.syncManager = syncManager;
|
||||
this.singletonManager = singletonManager;
|
||||
|
||||
this.extManagerId = "org.standardnotes.extensions-manager";
|
||||
this.batchManagerId = "org.standardnotes.batch-manager";
|
||||
constructor(application, appState) {
|
||||
this.application = application;
|
||||
this.extManagerId = 'org.standardnotes.extensions-manager';
|
||||
this.batchManagerId = 'org.standardnotes.batch-manager';
|
||||
this.systemExtensions = [];
|
||||
|
||||
this.resolveExtensionsManager();
|
||||
this.resolveBatchManager();
|
||||
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isSystemExtension(extension) {
|
||||
return this.systemExtensions.includes(extension.uuid);
|
||||
}
|
||||
|
||||
resolveExtensionsManager() {
|
||||
async initialize() {
|
||||
this.resolveExtensionsManager();
|
||||
this.resolveBatchManager();
|
||||
}
|
||||
|
||||
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component");
|
||||
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.extManagerId);
|
||||
|
||||
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => {
|
||||
// Resolved Singleton
|
||||
this.systemExtensions.push(resolvedSingleton.uuid);
|
||||
|
||||
var needsSync = false;
|
||||
if(isDesktopApplication()) {
|
||||
if(!resolvedSingleton.local_url) {
|
||||
resolvedSingleton.local_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if(!resolvedSingleton.hosted_url) {
|
||||
resolvedSingleton.hosted_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
extensionsManagerTemplatePayload() {
|
||||
const url = window._extensions_manager_location;
|
||||
if (!url) {
|
||||
console.error('window._extensions_manager_location must be set.');
|
||||
return;
|
||||
}
|
||||
const packageInfo = {
|
||||
name: 'Extensions',
|
||||
identifier: this.extManagerId
|
||||
};
|
||||
const content = {
|
||||
name: packageInfo.name,
|
||||
area: 'rooms',
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: STREAM_ITEMS_PERMISSION,
|
||||
content_types: [
|
||||
ContentTypes.Component,
|
||||
ContentTypes.Theme,
|
||||
ContentTypes.ServerExtension,
|
||||
ContentTypes.ActionsExtension,
|
||||
ContentTypes.Mfa,
|
||||
ContentTypes.Editor,
|
||||
ContentTypes.ExtensionRepo
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
if (isDesktopApplication()) {
|
||||
content.local_url = window._extensions_manager_location;
|
||||
} else {
|
||||
content.hosted_url = window._extensions_manager_location;
|
||||
}
|
||||
const payload = CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: ContentTypes.Component,
|
||||
content: content
|
||||
}
|
||||
});
|
||||
return payload;
|
||||
}
|
||||
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = resolvedSingleton.content.permissions.find((p) => p.name == "stream-items");
|
||||
if(!permission.content_types.includes("SN|ExtensionRepo")) {
|
||||
permission.content_types.push("SN|ExtensionRepo");
|
||||
async resolveExtensionsManager() {
|
||||
const contentTypePredicate = new SFPredicate('content_type', '=', ContentTypes.Component);
|
||||
const packagePredicate = new SFPredicate('package_info.identifier', '=', this.extManagerId);
|
||||
const predicate = SFPredicate.CompoundPredicate([contentTypePredicate, packagePredicate]);
|
||||
const extensionsManager = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: this.extensionsManagerTemplatePayload()
|
||||
});
|
||||
this.systemExtensions.push(extensionsManager.uuid);
|
||||
let needsSync = false;
|
||||
if (isDesktopApplication()) {
|
||||
if (!extensionsManager.local_url) {
|
||||
extensionsManager.local_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
|
||||
if(needsSync) {
|
||||
this.modelManager.setItemDirty(resolvedSingleton, true);
|
||||
this.syncManager.sync();
|
||||
} else {
|
||||
if (!extensionsManager.hosted_url) {
|
||||
extensionsManager.hosted_url = window._extensions_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
}, (valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const url = window._extensions_manager_location;
|
||||
if(!url) {
|
||||
console.error("window._extensions_manager_location must be set.");
|
||||
return;
|
||||
}
|
||||
|
||||
const packageInfo = {
|
||||
name: "Extensions",
|
||||
identifier: this.extManagerId
|
||||
};
|
||||
|
||||
var item = {
|
||||
content_type: "SN|Component",
|
||||
content: {
|
||||
name: packageInfo.name,
|
||||
area: "rooms",
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: "stream-items",
|
||||
content_types: [
|
||||
"SN|Component", "SN|Theme", "SF|Extension",
|
||||
"Extension", "SF|MFA", "SN|Editor", "SN|ExtensionRepo"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
item.content.local_url = window._extensions_manager_location;
|
||||
} else {
|
||||
item.content.hosted_url = window._extensions_manager_location;
|
||||
}
|
||||
|
||||
var component = this.modelManager.createItem(item);
|
||||
this.modelManager.addItem(component);
|
||||
|
||||
this.modelManager.setItemDirty(component, true);
|
||||
this.syncManager.sync();
|
||||
|
||||
this.systemExtensions.push(component.uuid);
|
||||
|
||||
valueCallback(component);
|
||||
});
|
||||
}
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = extensionsManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
||||
needsSync = true;
|
||||
}
|
||||
if (needsSync) {
|
||||
this.application.saveItem({ item: extensionsManager });
|
||||
}
|
||||
}
|
||||
|
||||
resolveBatchManager() {
|
||||
|
||||
const contentTypePredicate = new SFPredicate("content_type", "=", "SN|Component");
|
||||
const packagePredicate = new SFPredicate("package_info.identifier", "=", this.batchManagerId);
|
||||
|
||||
this.singletonManager.registerSingleton([contentTypePredicate, packagePredicate], (resolvedSingleton) => {
|
||||
// Resolved Singleton
|
||||
this.systemExtensions.push(resolvedSingleton.uuid);
|
||||
|
||||
var needsSync = false;
|
||||
if(isDesktopApplication()) {
|
||||
if(!resolvedSingleton.local_url) {
|
||||
resolvedSingleton.local_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if(!resolvedSingleton.hosted_url) {
|
||||
resolvedSingleton.hosted_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
batchManagerTemplatePayload() {
|
||||
const url = window._batch_manager_location;
|
||||
if (!url) {
|
||||
console.error('window._batch_manager_location must be set.');
|
||||
return;
|
||||
}
|
||||
const packageInfo = {
|
||||
name: 'Batch Manager',
|
||||
identifier: this.batchManagerId
|
||||
};
|
||||
const allContentTypes = dictToArray(ContentTypes);
|
||||
const content = {
|
||||
name: packageInfo.name,
|
||||
area: 'modal',
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: STREAM_ITEMS_PERMISSION,
|
||||
content_types: allContentTypes
|
||||
}
|
||||
]
|
||||
};
|
||||
if (isDesktopApplication()) {
|
||||
content.local_url = window._batch_manager_location;
|
||||
} else {
|
||||
content.hosted_url = window._batch_manager_location;
|
||||
}
|
||||
const payload = CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: ContentTypes.Component,
|
||||
content: content
|
||||
}
|
||||
|
||||
if(needsSync) {
|
||||
this.modelManager.setItemDirty(resolvedSingleton, true);
|
||||
this.syncManager.sync();
|
||||
}
|
||||
}, (valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const url = window._batch_manager_location;
|
||||
if(!url) {
|
||||
console.error("window._batch_manager_location must be set.");
|
||||
return;
|
||||
}
|
||||
|
||||
const packageInfo = {
|
||||
name: "Batch Manager",
|
||||
identifier: this.batchManagerId
|
||||
};
|
||||
|
||||
var item = {
|
||||
content_type: "SN|Component",
|
||||
content: {
|
||||
name: packageInfo.name,
|
||||
area: "modal",
|
||||
package_info: packageInfo,
|
||||
permissions: [
|
||||
{
|
||||
name: "stream-items",
|
||||
content_types: [
|
||||
"Note", "Tag", "SN|SmartTag",
|
||||
"SN|Component", "SN|Theme", "SN|UserPreferences",
|
||||
"SF|Extension", "Extension", "SF|MFA", "SN|Editor",
|
||||
"SN|FileSafe|Credentials", "SN|FileSafe|FileMetadata", "SN|FileSafe|Integration"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if(isDesktopApplication()) {
|
||||
item.content.local_url = window._batch_manager_location;
|
||||
} else {
|
||||
item.content.hosted_url = window._batch_manager_location;
|
||||
}
|
||||
|
||||
var component = this.modelManager.createItem(item);
|
||||
this.modelManager.addItem(component);
|
||||
|
||||
this.modelManager.setItemDirty(component, true);
|
||||
this.syncManager.sync();
|
||||
|
||||
this.systemExtensions.push(component.uuid);
|
||||
|
||||
valueCallback(component);
|
||||
});
|
||||
return payload;
|
||||
}
|
||||
|
||||
async resolveBatchManager() {
|
||||
const contentTypePredicate = new SFPredicate('content_type', '=', ContentTypes.Component);
|
||||
const packagePredicate = new SFPredicate('package_info.identifier', '=', this.batchManagerId);
|
||||
const predicate = SFPredicate.CompoundPredicate([contentTypePredicate, packagePredicate]);
|
||||
const batchManager = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: this.batchManagerTemplatePayload()
|
||||
});
|
||||
this.systemExtensions.push(batchManager.uuid);
|
||||
let needsSync = false;
|
||||
if (isDesktopApplication()) {
|
||||
if (!batchManager.local_url) {
|
||||
batchManager.local_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
} else {
|
||||
if (!batchManager.hosted_url) {
|
||||
batchManager.hosted_url = window._batch_manager_location;
|
||||
needsSync = true;
|
||||
}
|
||||
}
|
||||
// Handle addition of SN|ExtensionRepo permission
|
||||
const permission = batchManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
||||
needsSync = true;
|
||||
}
|
||||
if (needsSync) {
|
||||
this.application.saveItem({ item: batchManager });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import { SFPredicate, SFItem } from 'snjs';
|
||||
import { SFPredicate, CreateMaxPayloadFromAnyObject } from 'snjs';
|
||||
import { AppStateEvents } from '../state';
|
||||
|
||||
export const PREF_TAGS_PANEL_WIDTH = 'tagsPanelWidth';
|
||||
export const PREF_NOTES_PANEL_WIDTH = 'notesPanelWidth';
|
||||
export const PREF_EDITOR_WIDTH = 'editorWidth';
|
||||
export const PREF_EDITOR_LEFT = 'editorLeft';
|
||||
export const PREF_EDITOR_MONOSPACE_ENABLED = 'monospaceFont';
|
||||
export const PREF_EDITOR_SPELLCHECK = 'spellcheck';
|
||||
export const PREF_EDITOR_RESIZERS_ENABLED = 'marginResizersEnabled';
|
||||
export const PREF_SORT_NOTES_BY = 'sortBy';
|
||||
export const PREF_SORT_NOTES_REVERSE = 'sortReverse';
|
||||
export const PREF_NOTES_SHOW_ARCHIVED = 'showArchived';
|
||||
export const PREF_NOTES_HIDE_PINNED = 'hidePinned';
|
||||
export const PREF_NOTES_HIDE_NOTE_PREVIEW = 'hideNotePreview';
|
||||
export const PREF_NOTES_HIDE_DATE = 'hideDate';
|
||||
export const PREF_NOTES_HIDE_TAGS = 'hideTags';
|
||||
export const PrefKeys = {
|
||||
TagsPanelWidth: 'tagsPanelWidth',
|
||||
NotesPanelWidth: 'notesPanelWidth',
|
||||
EditorWidth: 'editorWidth',
|
||||
EditorLeft: 'editorLeft',
|
||||
EditorMonospaceEnabled: 'monospaceFont',
|
||||
EditorSpellcheck: 'spellcheck',
|
||||
EditorResizersEnabled: 'marginResizersEnabled',
|
||||
SortNotesBy: 'sortBy',
|
||||
SortNotesReverse: 'sortReverse',
|
||||
NotesShowArchived: 'showArchived',
|
||||
NotesHidePinned: 'hidePinned',
|
||||
NotesHideNotePreview: 'hideNotePreview',
|
||||
NotesHideDate: 'hideDate',
|
||||
NotesHideTags: 'hideTags'
|
||||
};
|
||||
|
||||
export class PreferencesManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
modelManager,
|
||||
singletonManager,
|
||||
appState,
|
||||
syncManager
|
||||
application
|
||||
) {
|
||||
this.singletonManager = singletonManager;
|
||||
this.modelManager = modelManager;
|
||||
this.syncManager = syncManager;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
|
||||
this.modelManager.addItemSyncObserver(
|
||||
'user-prefs',
|
||||
'SN|UserPreferences',
|
||||
(allItems, validItems, deletedItems, source, sourceKey) => {
|
||||
this.preferencesDidChange();
|
||||
appState.addObserver(async (eventName) => {
|
||||
if (eventName === AppStateEvents.ApplicationReady) {
|
||||
await this.initialize();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
load() {
|
||||
const prefsContentType = 'SN|UserPreferences';
|
||||
const contentTypePredicate = new SFPredicate(
|
||||
'content_type',
|
||||
'=',
|
||||
prefsContentType
|
||||
);
|
||||
this.singletonManager.registerSingleton(
|
||||
[contentTypePredicate],
|
||||
(resolvedSingleton) => {
|
||||
this.userPreferences = resolvedSingleton;
|
||||
},
|
||||
(valueCallback) => {
|
||||
// Safe to create. Create and return object.
|
||||
const prefs = new SFItem({content_type: prefsContentType});
|
||||
this.modelManager.addItem(prefs);
|
||||
this.modelManager.setItemDirty(prefs);
|
||||
this.syncManager.sync();
|
||||
valueCallback(prefs);
|
||||
async initialize() {
|
||||
this.streamPreferences();
|
||||
await this.loadSingleton();
|
||||
}
|
||||
|
||||
streamPreferences() {
|
||||
this.application.streamItems({
|
||||
contentType: 'SN|UserPreferences',
|
||||
stream: () => {
|
||||
this.preferencesDidChange();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async loadSingleton() {
|
||||
const contentType = 'SN|UserPreferences';
|
||||
const predicate = new SFPredicate('content_type', '=', contentType);
|
||||
this.userPreferences = await this.application.singletonManager.findOrCreateSingleton({
|
||||
predicate: predicate,
|
||||
createPayload: CreateMaxPayloadFromAnyObject({
|
||||
object: {
|
||||
content_type: contentType
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
preferencesDidChange() {
|
||||
@@ -65,21 +65,20 @@ export class PreferencesManager {
|
||||
}
|
||||
|
||||
syncUserPreferences() {
|
||||
if(this.userPreferences) {
|
||||
this.modelManager.setItemDirty(this.userPreferences);
|
||||
this.syncManager.sync();
|
||||
if (this.userPreferences) {
|
||||
this.application.saveItem({item: this.userPreferences});
|
||||
}
|
||||
}
|
||||
|
||||
getValue(key, defaultValue) {
|
||||
if(!this.userPreferences) { return defaultValue; }
|
||||
if (!this.userPreferences) { return defaultValue; }
|
||||
const value = this.userPreferences.getAppDataItem(key);
|
||||
return (value !== undefined && value != null) ? value : defaultValue;
|
||||
}
|
||||
|
||||
setUserPrefValue(key, value, sync) {
|
||||
this.userPreferences.setAppDataItem(key, value);
|
||||
if(sync) {
|
||||
if (sync) {
|
||||
this.syncUserPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,28 @@
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { SNTheme, SFItemParams } from 'snjs';
|
||||
import { StorageManager } from './storageManager';
|
||||
import {
|
||||
APP_STATE_EVENT_DESKTOP_EXTS_READY
|
||||
} from '@/state';
|
||||
import { SNTheme, StorageValueModes, EncryptionIntents } from 'snjs';
|
||||
import { AppStateEvents } from '@/state';
|
||||
|
||||
const CACHED_THEMES_KEY = 'cachedThemes';
|
||||
|
||||
export class ThemeManager {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
componentManager,
|
||||
application,
|
||||
appState,
|
||||
desktopManager,
|
||||
storageManager,
|
||||
lockManager,
|
||||
appState
|
||||
) {
|
||||
this.componentManager = componentManager;
|
||||
this.storageManager = storageManager;
|
||||
this.application = application;
|
||||
this.appState = appState;
|
||||
this.desktopManager = desktopManager;
|
||||
this.activeThemes = [];
|
||||
|
||||
ThemeManager.CachedThemesKey = "cachedThemes";
|
||||
|
||||
this.registerObservers();
|
||||
|
||||
if (desktopManager.isDesktop) {
|
||||
appState.addObserver((eventName, data) => {
|
||||
if (eventName === APP_STATE_EVENT_DESKTOP_EXTS_READY) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!desktopManager.isDesktop) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
}
|
||||
|
||||
activateCachedThemes() {
|
||||
const cachedThemes = this.getCachedThemes();
|
||||
async activateCachedThemes() {
|
||||
const cachedThemes = await this.getCachedThemes();
|
||||
const writeToCache = false;
|
||||
for (const theme of cachedThemes) {
|
||||
this.activateTheme(theme, writeToCache);
|
||||
@@ -44,6 +30,11 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
registerObservers() {
|
||||
this.appState.addObserver((eventName, data) => {
|
||||
if (eventName === AppStateEvents.DesktopExtsReady) {
|
||||
this.activateCachedThemes();
|
||||
}
|
||||
});
|
||||
this.desktopManager.registerUpdateObserver((component) => {
|
||||
// Reload theme if active
|
||||
if (component.active && component.isTheme()) {
|
||||
@@ -54,9 +45,9 @@ export class ThemeManager {
|
||||
}
|
||||
});
|
||||
|
||||
this.componentManager.registerHandler({
|
||||
identifier: "themeManager",
|
||||
areas: ["themes"],
|
||||
this.application.componentManager.registerHandler({
|
||||
identifier: 'themeManager',
|
||||
areas: ['themes'],
|
||||
activationHandler: (component) => {
|
||||
if (component.active) {
|
||||
this.activateTheme(component);
|
||||
@@ -68,14 +59,14 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
hasActiveTheme() {
|
||||
return this.componentManager.getActiveThemes().length > 0;
|
||||
return this.application.componentManager.getActiveThemes().length > 0;
|
||||
}
|
||||
|
||||
deactivateAllThemes() {
|
||||
var activeThemes = this.componentManager.getActiveThemes();
|
||||
var activeThemes = this.application.componentManager.getActiveThemes();
|
||||
for (var theme of activeThemes) {
|
||||
if (theme) {
|
||||
this.componentManager.deactivateComponent(theme);
|
||||
this.application.componentManager.deactivateComponent(theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,25 +77,22 @@ export class ThemeManager {
|
||||
if (_.find(this.activeThemes, { uuid: theme.uuid })) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeThemes.push(theme);
|
||||
|
||||
var url = this.componentManager.urlForComponent(theme);
|
||||
var link = document.createElement("link");
|
||||
const url = this.application.componentManager.urlForComponent(theme);
|
||||
const link = document.createElement('link');
|
||||
link.href = url;
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.media = "screen,print";
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.media = 'screen,print';
|
||||
link.id = theme.uuid;
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
if (writeToCache) {
|
||||
this.cacheThemes();
|
||||
}
|
||||
}
|
||||
|
||||
deactivateTheme(theme) {
|
||||
var element = document.getElementById(theme.uuid);
|
||||
const element = document.getElementById(theme.uuid);
|
||||
if (element) {
|
||||
element.disabled = true;
|
||||
element.parentNode.removeChild(element);
|
||||
@@ -117,20 +105,33 @@ export class ThemeManager {
|
||||
|
||||
async cacheThemes() {
|
||||
const mapped = await Promise.all(this.activeThemes.map(async (theme) => {
|
||||
const transformer = new SFItemParams(theme);
|
||||
const params = await transformer.paramsForLocalStorage();
|
||||
return params;
|
||||
const payload = theme.payloadRepresentation();
|
||||
const processedPayload = await this.application.protocolService.payloadByEncryptingPayload({
|
||||
payload: payload,
|
||||
intent: EncryptionIntents.LocalStorageDecrypted
|
||||
});
|
||||
return processedPayload;
|
||||
}));
|
||||
const data = JSON.stringify(mapped);
|
||||
return this.storageManager.setItem(ThemeManager.CachedThemesKey, data, StorageManager.Fixed);
|
||||
return this.application.setValue(
|
||||
CACHED_THEMES_KEY,
|
||||
data,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
}
|
||||
|
||||
async decacheThemes() {
|
||||
return this.storageManager.removeItem(ThemeManager.CachedThemesKey, StorageManager.Fixed);
|
||||
return this.application.removeValue(
|
||||
CACHED_THEMES_KEY,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
}
|
||||
|
||||
getCachedThemes() {
|
||||
const cachedThemes = this.storageManager.getItemSync(ThemeManager.CachedThemesKey, StorageManager.Fixed);
|
||||
async getCachedThemes() {
|
||||
const cachedThemes = await this.application.getValue(
|
||||
CACHED_THEMES_KEY,
|
||||
StorageValueModes.Nonwrapped
|
||||
);
|
||||
if (cachedThemes) {
|
||||
const parsed = JSON.parse(cachedThemes);
|
||||
return parsed.map((theme) => {
|
||||
|
||||
Reference in New Issue
Block a user