1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-03 23:50:33 +02:00

Tools: Add class member accessibility modifiers and converted rule @typescript-eslint/explicit-member-accessibility to an error

This commit is contained in:
Laurent Cozic
2023-03-06 14:22:01 +00:00
parent aa4af69afc
commit c1db7182ac
129 changed files with 1252 additions and 1296 deletions

View File

@ -175,9 +175,7 @@ module.exports = {
'project': './tsconfig.eslint.json', 'project': './tsconfig.eslint.json',
}, },
'rules': { 'rules': {
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we '@typescript-eslint/explicit-member-accessibility': ['error'],
// make everything public which is not great. New code however should specify member accessibility.
'@typescript-eslint/explicit-member-accessibility': ['warn'],
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }], '@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
'@typescript-eslint/no-inferrable-types': ['error', { 'ignoreParameters': true, 'ignoreProperties': true }], '@typescript-eslint/no-inferrable-types': ['error', { 'ignoreParameters': true, 'ignoreProperties': true }],
'@typescript-eslint/comma-dangle': ['error', { '@typescript-eslint/comma-dangle': ['error', {

View File

@ -6,14 +6,14 @@ interface LinkStoreEntry {
} }
class LinkSelector { class LinkSelector {
noteId_: string; private noteId_: string;
scrollTop_: number; private scrollTop_: number;
renderedText_: string; private renderedText_: string;
currentLinkIndex_: number; private currentLinkIndex_: number;
linkStore_: LinkStoreEntry[]; private linkStore_: LinkStoreEntry[];
linkRegex_: RegExp; private linkRegex_: RegExp;
constructor() { public constructor() {
this.noteId_ = null; this.noteId_ = null;
this.scrollTop_ = null; // used so 'o' won't open unhighlighted link after scrolling this.scrollTop_ = null; // used so 'o' won't open unhighlighted link after scrolling
this.renderedText_ = null; this.renderedText_ = null;
@ -22,22 +22,22 @@ class LinkSelector {
this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g; this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g;
} }
get link(): string | null { public get link(): string | null {
if (this.currentLinkIndex_ === null) return null; if (this.currentLinkIndex_ === null) return null;
return this.linkStore_[this.currentLinkIndex_].link; return this.linkStore_[this.currentLinkIndex_].link;
} }
get noteX(): number | null { public get noteX(): number | null {
if (this.currentLinkIndex_ === null) return null; if (this.currentLinkIndex_ === null) return null;
return this.linkStore_[this.currentLinkIndex_].noteX; return this.linkStore_[this.currentLinkIndex_].noteX;
} }
get noteY(): number | null { public get noteY(): number | null {
if (this.currentLinkIndex_ === null) return null; if (this.currentLinkIndex_ === null) return null;
return this.linkStore_[this.currentLinkIndex_].noteY; return this.linkStore_[this.currentLinkIndex_].noteY;
} }
findLinks(renderedText: string): LinkStoreEntry[] { public findLinks(renderedText: string): LinkStoreEntry[] {
const newLinkStore: LinkStoreEntry[] = []; const newLinkStore: LinkStoreEntry[] = [];
const lines: string[] = renderedText.split('\n'); const lines: string[] = renderedText.split('\n');
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
@ -56,19 +56,19 @@ class LinkSelector {
return newLinkStore; return newLinkStore;
} }
updateText(renderedText: string): void { public updateText(renderedText: string): void {
this.currentLinkIndex_ = null; this.currentLinkIndex_ = null;
this.renderedText_ = renderedText; this.renderedText_ = renderedText;
this.linkStore_ = this.findLinks(this.renderedText_); this.linkStore_ = this.findLinks(this.renderedText_);
} }
updateNote(textWidget: any): void { public updateNote(textWidget: any): void {
this.noteId_ = textWidget.noteId; this.noteId_ = textWidget.noteId;
this.scrollTop_ = textWidget.scrollTop_; this.scrollTop_ = textWidget.scrollTop_;
this.updateText(textWidget.renderedText_); this.updateText(textWidget.renderedText_);
} }
scrollWidget(textWidget: any): void { public scrollWidget(textWidget: any): void {
if (this.currentLinkIndex_ === null) return; if (this.currentLinkIndex_ === null) return;
const noteY = this.linkStore_[this.currentLinkIndex_].noteY; const noteY = this.linkStore_[this.currentLinkIndex_].noteY;
@ -93,7 +93,7 @@ class LinkSelector {
return; return;
} }
changeLink(textWidget: any, offset: number): void | null { public changeLink(textWidget: any, offset: number): void | null {
if (textWidget.noteId !== this.noteId_) { if (textWidget.noteId !== this.noteId_) {
this.updateNote(textWidget); this.updateNote(textWidget);
this.changeLink(textWidget, offset); this.changeLink(textWidget, offset);
@ -123,7 +123,7 @@ class LinkSelector {
return; return;
} }
openLink(textWidget: any): void { public openLink(textWidget: any): void {
if (textWidget.noteId !== this.noteId_) return; if (textWidget.noteId !== this.noteId_) return;
if (textWidget.renderedText_ !== this.renderedText_) return; if (textWidget.renderedText_ !== this.renderedText_) return;
if (textWidget.scrollTop_ !== this.scrollTop_) return; if (textWidget.scrollTop_ !== this.scrollTop_) return;

View File

@ -7,80 +7,80 @@ export default class BaseCommand {
protected prompt_: any = null; protected prompt_: any = null;
protected dispatcher_: any; protected dispatcher_: any;
usage(): string { public usage(): string {
throw new Error('Usage not defined'); throw new Error('Usage not defined');
} }
encryptionCheck(item: any) { public encryptionCheck(item: any) {
if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item')); if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item'));
} }
description() { public description() {
throw new Error('Description not defined'); throw new Error('Description not defined');
} }
async action(_args: any) { public async action(_args: any) {
throw new Error('Action not defined'); throw new Error('Action not defined');
} }
compatibleUis() { public compatibleUis() {
return ['cli', 'gui']; return ['cli', 'gui'];
} }
supportsUi(ui: string) { public supportsUi(ui: string) {
return this.compatibleUis().indexOf(ui) >= 0; return this.compatibleUis().indexOf(ui) >= 0;
} }
options(): any[] { public options(): any[] {
return []; return [];
} }
hidden() { public hidden() {
return false; return false;
} }
enabled() { public enabled() {
return true; return true;
} }
cancellable() { public cancellable() {
return false; return false;
} }
async cancel() {} public async cancel() {}
name() { public name() {
const r = this.usage().split(' '); const r = this.usage().split(' ');
return r[0]; return r[0];
} }
setDispatcher(fn: Function) { public setDispatcher(fn: Function) {
this.dispatcher_ = fn; this.dispatcher_ = fn;
} }
dispatch(action: any) { public dispatch(action: any) {
if (!this.dispatcher_) throw new Error('Dispatcher not defined'); if (!this.dispatcher_) throw new Error('Dispatcher not defined');
return this.dispatcher_(action); return this.dispatcher_(action);
} }
setStdout(fn: Function) { public setStdout(fn: Function) {
this.stdout_ = fn; this.stdout_ = fn;
} }
stdout(text: string) { public stdout(text: string) {
if (this.stdout_) this.stdout_(text); if (this.stdout_) this.stdout_(text);
} }
setPrompt(fn: Function) { public setPrompt(fn: Function) {
this.prompt_ = fn; this.prompt_ = fn;
} }
async prompt(message: string, options: any = null) { public async prompt(message: string, options: any = null) {
if (!this.prompt_) throw new Error('Prompt is undefined'); if (!this.prompt_) throw new Error('Prompt is undefined');
return await this.prompt_(message, options); return await this.prompt_(message, options);
} }
metadata() { public metadata() {
return { return {
name: this.name(), name: this.name(),
usage: this.usage(), usage: this.usage(),
@ -89,7 +89,7 @@ export default class BaseCommand {
}; };
} }
logger() { public logger() {
return reg.logger(); return reg.logger();
} }
} }

View File

@ -12,15 +12,15 @@ const imageType = require('image-type');
const readChunk = require('read-chunk'); const readChunk = require('read-chunk');
class Command extends BaseCommand { class Command extends BaseCommand {
usage() { public usage() {
return 'e2ee <command> [path]'; return 'e2ee <command> [path]';
} }
description() { public description() {
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status`, `decrypt-file`, and `target-status`.'); // `generate-ppk` return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status`, `decrypt-file`, and `target-status`.'); // `generate-ppk`
} }
options() { public options() {
return [ return [
// This is here mostly for testing - shouldn't be used // This is here mostly for testing - shouldn't be used
['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'], ['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'],
@ -30,7 +30,7 @@ class Command extends BaseCommand {
]; ];
} }
async action(args: any) { public async action(args: any) {
const options = args.options; const options = args.options;
const askForMasterKey = async (error: any) => { const askForMasterKey = async (error: any) => {

View File

@ -6,22 +6,22 @@ import Folder from '@joplin/lib/models/Folder';
import { FolderEntity } from '@joplin/lib/services/database/types'; import { FolderEntity } from '@joplin/lib/services/database/types';
class Command extends BaseCommand { class Command extends BaseCommand {
usage() { public usage() {
return 'mkbook <new-notebook>'; return 'mkbook <new-notebook>';
} }
description() { public description() {
return _('Creates a new notebook.'); return _('Creates a new notebook.');
} }
options() { public options() {
return [ return [
['-p, --parent <parent-notebook>', _('Create a new notebook under a parent notebook.')], ['-p, --parent <parent-notebook>', _('Create a new notebook under a parent notebook.')],
]; ];
} }
// validDestinationFolder check for presents and ambiguous folders // validDestinationFolder check for presents and ambiguous folders
async validDestinationFolder(targetFolder: string) { public async validDestinationFolder(targetFolder: string) {
const destinationFolder = await app().loadItem(BaseModel.TYPE_FOLDER, targetFolder); const destinationFolder = await app().loadItem(BaseModel.TYPE_FOLDER, targetFolder);
if (!destinationFolder) { if (!destinationFolder) {
@ -36,14 +36,14 @@ class Command extends BaseCommand {
return destinationFolder; return destinationFolder;
} }
async saveAndSwitchFolder(newFolder: FolderEntity) { public async saveAndSwitchFolder(newFolder: FolderEntity) {
const folder = await Folder.save(newFolder, { userSideValidation: true }); const folder = await Folder.save(newFolder, { userSideValidation: true });
app().switchCurrentFolder(folder); app().switchCurrentFolder(folder);
} }
async action(args: any) { public async action(args: any) {
const targetFolder = args.options.parent; const targetFolder = args.options.parent;
const newFolder: FolderEntity = { const newFolder: FolderEntity = {

View File

@ -23,19 +23,19 @@ function settingTypeToSchemaType(type: SettingItemType): string {
} }
class Command extends BaseCommand { class Command extends BaseCommand {
usage() { public usage() {
return 'settingschema <file>'; return 'settingschema <file>';
} }
description() { public description() {
return 'Build the setting schema file'; return 'Build the setting schema file';
} }
enabled() { public enabled() {
return false; return false;
} }
async action(args: any) { public async action(args: any) {
const schema: Record<string, any> = { const schema: Record<string, any> = {
title: 'JSON schema for Joplin setting files', title: 'JSON schema for Joplin setting files',
'$id': Setting.schemaUrl, '$id': Setting.schemaUrl,

View File

@ -21,15 +21,15 @@ class Command extends BaseCommand {
private releaseLockFn_: Function = null; private releaseLockFn_: Function = null;
private oneDriveApiUtils_: any = null; private oneDriveApiUtils_: any = null;
usage() { public usage() {
return 'sync'; return 'sync';
} }
description() { public description() {
return _('Synchronises with remote storage.'); return _('Synchronises with remote storage.');
} }
options() { public options() {
return [ return [
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')], ['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
['--upgrade', _('Upgrade the sync target to the latest version.')], ['--upgrade', _('Upgrade the sync target to the latest version.')],
@ -45,7 +45,7 @@ class Command extends BaseCommand {
return locker.check(filePath); return locker.check(filePath);
} }
async doAuth() { public async doAuth() {
const syncTarget = reg.syncTarget(this.syncTargetId_); const syncTarget = reg.syncTarget(this.syncTargetId_);
const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_); const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_);
@ -89,18 +89,18 @@ class Command extends BaseCommand {
return false; return false;
} }
cancelAuth() { public cancelAuth() {
if (this.oneDriveApiUtils_) { if (this.oneDriveApiUtils_) {
this.oneDriveApiUtils_.cancelOAuthDance(); this.oneDriveApiUtils_.cancelOAuthDance();
return; return;
} }
} }
doingAuth() { public doingAuth() {
return !!this.oneDriveApiUtils_; return !!this.oneDriveApiUtils_;
} }
async action(args: any) { public async action(args: any) {
this.releaseLockFn_ = null; this.releaseLockFn_ = null;
// Lock is unique per profile/database // Lock is unique per profile/database
@ -238,7 +238,7 @@ class Command extends BaseCommand {
cleanUp(); cleanUp();
} }
async cancel() { public async cancel() {
if (this.doingAuth()) { if (this.doingAuth()) {
this.cancelAuth(); this.cancelAuth();
return; return;
@ -263,7 +263,7 @@ class Command extends BaseCommand {
this.syncTargetId_ = null; this.syncTargetId_ = null;
} }
cancellable() { public cancellable() {
return true; return true;
} }
} }

View File

@ -18,19 +18,19 @@ function itemCount(args: any) {
} }
class Command extends BaseCommand { class Command extends BaseCommand {
usage() { public usage() {
return 'testing <command> [arg0]'; return 'testing <command> [arg0]';
} }
description() { public description() {
return 'testing'; return 'testing';
} }
enabled() { public enabled() {
return false; return false;
} }
options(): any[] { public options(): any[] {
return [ return [
['--folder-count <count>', 'Folders to create'], ['--folder-count <count>', 'Folders to create'],
['--note-count <count>', 'Notes to create'], ['--note-count <count>', 'Notes to create'],
@ -40,7 +40,7 @@ class Command extends BaseCommand {
]; ];
} }
async action(args: any) { public async action(args: any) {
const { command, options } = args; const { command, options } = args;
if (command === 'populate') { if (command === 'populate') {

View File

@ -5,7 +5,7 @@ const stripAnsi = require('strip-ansi');
const { handleAutocompletion } = require('../autocompletion.js'); const { handleAutocompletion } = require('../autocompletion.js');
export default class StatusBarWidget extends BaseWidget { export default class StatusBarWidget extends BaseWidget {
constructor() { public constructor() {
super(); super();
this.promptState_ = null; this.promptState_ = null;
@ -14,20 +14,20 @@ export default class StatusBarWidget extends BaseWidget {
this.items_ = []; this.items_ = [];
} }
get name() { public get name() {
return 'statusBar'; return 'statusBar';
} }
get canHaveFocus() { public get canHaveFocus() {
return false; return false;
} }
setItemAt(index: number, text: string) { public setItemAt(index: number, text: string) {
this.items_[index] = stripAnsi(text).trim(); this.items_[index] = stripAnsi(text).trim();
this.invalidate(); this.invalidate();
} }
async prompt(initialText = '', promptString: any = null, options: any = null) { public async prompt(initialText = '', promptString: any = null, options: any = null) {
if (this.promptState_) throw new Error('Another prompt already active'); if (this.promptState_) throw new Error('Another prompt already active');
if (promptString === null) promptString = ':'; if (promptString === null) promptString = ':';
if (options === null) options = {}; if (options === null) options = {};
@ -53,15 +53,15 @@ export default class StatusBarWidget extends BaseWidget {
return this.promptState_.promise; return this.promptState_.promise;
} }
get promptActive() { public get promptActive() {
return !!this.promptState_; return !!this.promptState_;
} }
get history() { public get history() {
return this.history_; return this.history_;
} }
resetCursor() { public resetCursor() {
if (!this.promptActive) return; if (!this.promptActive) return;
if (!this.inputEventEmitter_) return; if (!this.inputEventEmitter_) return;
@ -70,7 +70,7 @@ export default class StatusBarWidget extends BaseWidget {
this.term.moveTo(this.absoluteInnerX + termutils.textLength(this.promptState_.promptString) + this.inputEventEmitter_.getInput().length, this.absoluteInnerY); this.term.moveTo(this.absoluteInnerX + termutils.textLength(this.promptState_.promptString) + this.inputEventEmitter_.getInput().length, this.absoluteInnerY);
} }
render() { public render() {
super.render(); super.render();
const doSaveCursor = !this.promptActive; const doSaveCursor = !this.promptActive;

View File

@ -35,7 +35,7 @@ export default class PluginRunner extends BasePluginRunner {
private eventHandlers_: EventHandlers = {}; private eventHandlers_: EventHandlers = {};
private activeSandboxCalls_: any = {}; private activeSandboxCalls_: any = {};
constructor() { public constructor() {
super(); super();
this.eventHandler = this.eventHandler.bind(this); this.eventHandler = this.eventHandler.bind(this);
@ -64,7 +64,7 @@ export default class PluginRunner extends BasePluginRunner {
}; };
} }
async run(plugin: Plugin, sandbox: Global): Promise<void> { public async run(plugin: Plugin, sandbox: Global): Promise<void> {
return new Promise((resolve: Function, reject: Function) => { return new Promise((resolve: Function, reject: Function) => {
const onStarted = () => { const onStarted = () => {
plugin.off('started', onStarted); plugin.off('started', onStarted);

View File

@ -33,7 +33,7 @@ export default class ElectronAppWrapper {
private pluginWindows_: PluginWindows = {}; private pluginWindows_: PluginWindows = {};
private initialCallbackUrl_: string = null; private initialCallbackUrl_: string = null;
constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) { public constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) {
this.electronApp_ = electronApp; this.electronApp_ = electronApp;
this.env_ = env; this.env_ = env;
this.isDebugMode_ = isDebugMode; this.isDebugMode_ = isDebugMode;
@ -41,31 +41,31 @@ export default class ElectronAppWrapper {
this.initialCallbackUrl_ = initialCallbackUrl; this.initialCallbackUrl_ = initialCallbackUrl;
} }
electronApp() { public electronApp() {
return this.electronApp_; return this.electronApp_;
} }
setLogger(v: Logger) { public setLogger(v: Logger) {
this.logger_ = v; this.logger_ = v;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
window() { public window() {
return this.win_; return this.win_;
} }
env() { public env() {
return this.env_; return this.env_;
} }
initialCallbackUrl() { public initialCallbackUrl() {
return this.initialCallbackUrl_; return this.initialCallbackUrl_;
} }
createWindow() { public createWindow() {
// Set to true to view errors if the application does not start // Set to true to view errors if the application does not start
const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_; const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_;
@ -236,11 +236,11 @@ export default class ElectronAppWrapper {
} }
} }
registerPluginWindow(pluginId: string, window: any) { public registerPluginWindow(pluginId: string, window: any) {
this.pluginWindows_[pluginId] = window; this.pluginWindows_[pluginId] = window;
} }
async waitForElectronAppReady() { public async waitForElectronAppReady() {
if (this.electronApp().isReady()) return Promise.resolve(); if (this.electronApp().isReady()) return Promise.resolve();
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
@ -253,25 +253,25 @@ export default class ElectronAppWrapper {
}); });
} }
quit() { public quit() {
this.electronApp_.quit(); this.electronApp_.quit();
} }
exit(errorCode = 0) { public exit(errorCode = 0) {
this.electronApp_.exit(errorCode); this.electronApp_.exit(errorCode);
} }
trayShown() { public trayShown() {
return !!this.tray_; return !!this.tray_;
} }
// This method is used in macOS only to hide the whole app (and not just the main window) // This method is used in macOS only to hide the whole app (and not just the main window)
// including the menu bar. This follows the macOS way of hiding an app. // including the menu bar. This follows the macOS way of hiding an app.
hide() { public hide() {
this.electronApp_.hide(); this.electronApp_.hide();
} }
buildDir() { public buildDir() {
if (this.buildDir_) return this.buildDir_; if (this.buildDir_) return this.buildDir_;
let dir = `${__dirname}/build`; let dir = `${__dirname}/build`;
if (!fs.pathExistsSync(dir)) { if (!fs.pathExistsSync(dir)) {
@ -283,7 +283,7 @@ export default class ElectronAppWrapper {
return dir; return dir;
} }
trayIconFilename_() { private trayIconFilename_() {
let output = ''; let output = '';
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@ -298,7 +298,7 @@ export default class ElectronAppWrapper {
} }
// Note: this must be called only after the "ready" event of the app has been dispatched // Note: this must be called only after the "ready" event of the app has been dispatched
createTray(contextMenu: any) { public createTray(contextMenu: any) {
try { try {
this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`); this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`);
this.tray_.setToolTip(this.electronApp_.name); this.tray_.setToolTip(this.electronApp_.name);
@ -312,13 +312,13 @@ export default class ElectronAppWrapper {
} }
} }
destroyTray() { public destroyTray() {
if (!this.tray_) return; if (!this.tray_) return;
this.tray_.destroy(); this.tray_.destroy();
this.tray_ = null; this.tray_ = null;
} }
ensureSingleInstance() { public ensureSingleInstance() {
if (this.env_ === 'dev') return false; if (this.env_ === 'dev') return false;
const gotTheLock = this.electronApp_.requestSingleInstanceLock(); const gotTheLock = this.electronApp_.requestSingleInstanceLock();
@ -347,7 +347,7 @@ export default class ElectronAppWrapper {
return false; return false;
} }
async start() { public async start() {
// Since we are doing other async things before creating the window, we might miss // Since we are doing other async things before creating the window, we might miss
// the "ready" event. So we use the function below to make sure that the app is ready. // the "ready" event. So we use the function below to make sure that the app is ready.
await this.waitForElectronAppReady(); await this.waitForElectronAppReady();
@ -375,7 +375,7 @@ export default class ElectronAppWrapper {
}); });
} }
async openCallbackUrl(url: string) { public async openCallbackUrl(url: string) {
this.win_.webContents.send('asynchronous-message', 'openCallbackUrl', { this.win_.webContents.send('asynchronous-message', 'openCallbackUrl', {
url: url, url: url,
}); });

View File

@ -21,7 +21,7 @@ export class Bridge {
private electronWrapper_: ElectronAppWrapper; private electronWrapper_: ElectronAppWrapper;
private lastSelectedPaths_: LastSelectedPath; private lastSelectedPaths_: LastSelectedPath;
constructor(electronWrapper: ElectronAppWrapper) { public constructor(electronWrapper: ElectronAppWrapper) {
this.electronWrapper_ = electronWrapper; this.electronWrapper_ = electronWrapper;
this.lastSelectedPaths_ = { this.lastSelectedPaths_ = {
file: null, file: null,
@ -29,11 +29,11 @@ export class Bridge {
}; };
} }
electronApp() { public electronApp() {
return this.electronWrapper_; return this.electronWrapper_;
} }
electronIsDev() { public electronIsDev() {
return !this.electronApp().electronApp().isPackaged; return !this.electronApp().electronApp().isPackaged;
} }
@ -60,11 +60,11 @@ export class Bridge {
return `${__dirname}/vendor`; return `${__dirname}/vendor`;
} }
env() { public env() {
return this.electronWrapper_.env(); return this.electronWrapper_.env();
} }
processArgv() { public processArgv() {
return process.argv; return process.argv;
} }
@ -114,44 +114,44 @@ export class Bridge {
}); });
} }
window() { public window() {
return this.electronWrapper_.window(); return this.electronWrapper_.window();
} }
showItemInFolder(fullPath: string) { public showItemInFolder(fullPath: string) {
return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath)); return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath));
} }
newBrowserWindow(options: any) { public newBrowserWindow(options: any) {
return new BrowserWindow(options); return new BrowserWindow(options);
} }
windowContentSize() { public windowContentSize() {
if (!this.window()) return { width: 0, height: 0 }; if (!this.window()) return { width: 0, height: 0 };
const s = this.window().getContentSize(); const s = this.window().getContentSize();
return { width: s[0], height: s[1] }; return { width: s[0], height: s[1] };
} }
windowSize() { public windowSize() {
if (!this.window()) return { width: 0, height: 0 }; if (!this.window()) return { width: 0, height: 0 };
const s = this.window().getSize(); const s = this.window().getSize();
return { width: s[0], height: s[1] }; return { width: s[0], height: s[1] };
} }
windowSetSize(width: number, height: number) { public windowSetSize(width: number, height: number) {
if (!this.window()) return; if (!this.window()) return;
return this.window().setSize(width, height); return this.window().setSize(width, height);
} }
openDevTools() { public openDevTools() {
return this.window().webContents.openDevTools(); return this.window().webContents.openDevTools();
} }
closeDevTools() { public closeDevTools() {
return this.window().webContents.closeDevTools(); return this.window().webContents.closeDevTools();
} }
async showSaveDialog(options: any) { public async showSaveDialog(options: any) {
const { dialog } = require('electron'); const { dialog } = require('electron');
if (!options) options = {}; if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file; if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file;
@ -162,7 +162,7 @@ export class Bridge {
return filePath; return filePath;
} }
async showOpenDialog(options: OpenDialogOptions = null) { public async showOpenDialog(options: OpenDialogOptions = null) {
const { dialog } = require('electron'); const { dialog } = require('electron');
if (!options) options = {}; if (!options) options = {};
let fileType = 'file'; let fileType = 'file';
@ -177,13 +177,13 @@ export class Bridge {
} }
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead // Don't use this directly - call one of the showXxxxxxxMessageBox() instead
showMessageBox_(window: any, options: any): number { private showMessageBox_(window: any, options: any): number {
const { dialog } = require('electron'); const { dialog } = require('electron');
if (!window) window = this.window(); if (!window) window = this.window();
return dialog.showMessageBoxSync(window, options); return dialog.showMessageBoxSync(window, options);
} }
showErrorMessageBox(message: string) { public showErrorMessageBox(message: string) {
return this.showMessageBox_(this.window(), { return this.showMessageBox_(this.window(), {
type: 'error', type: 'error',
message: message, message: message,
@ -191,7 +191,7 @@ export class Bridge {
}); });
} }
showConfirmMessageBox(message: string, options: any = null) { public showConfirmMessageBox(message: string, options: any = null) {
options = { options = {
buttons: [_('OK'), _('Cancel')], buttons: [_('OK'), _('Cancel')],
...options, ...options,
@ -208,7 +208,7 @@ export class Bridge {
} }
/* returns the index of the clicked button */ /* returns the index of the clicked button */
showMessageBox(message: string, options: any = null) { public showMessageBox(message: string, options: any = null) {
if (options === null) options = {}; if (options === null) options = {};
const result = this.showMessageBox_(this.window(), Object.assign({}, { const result = this.showMessageBox_(this.window(), Object.assign({}, {
@ -220,7 +220,7 @@ export class Bridge {
return result; return result;
} }
showInfoMessageBox(message: string, options: any = {}) { public showInfoMessageBox(message: string, options: any = {}) {
const result = this.showMessageBox_(this.window(), Object.assign({}, { const result = this.showMessageBox_(this.window(), Object.assign({}, {
type: 'info', type: 'info',
message: message, message: message,
@ -229,35 +229,35 @@ export class Bridge {
return result === 0; return result === 0;
} }
setLocale(locale: string) { public setLocale(locale: string) {
setLocale(locale); setLocale(locale);
} }
get Menu() { public get Menu() {
return require('electron').Menu; return require('electron').Menu;
} }
get MenuItem() { public get MenuItem() {
return require('electron').MenuItem; return require('electron').MenuItem;
} }
openExternal(url: string) { public openExternal(url: string) {
return require('electron').shell.openExternal(url); return require('electron').shell.openExternal(url);
} }
async openItem(fullPath: string) { public async openItem(fullPath: string) {
return require('electron').shell.openPath(toSystemSlashes(fullPath)); return require('electron').shell.openPath(toSystemSlashes(fullPath));
} }
screen() { public screen() {
return require('electron').screen; return require('electron').screen;
} }
shouldUseDarkColors() { public shouldUseDarkColors() {
return nativeTheme.shouldUseDarkColors; return nativeTheme.shouldUseDarkColors;
} }
addEventListener(name: string, fn: Function) { public addEventListener(name: string, fn: Function) {
if (name === 'nativeThemeUpdated') { if (name === 'nativeThemeUpdated') {
nativeTheme.on('updated', fn); nativeTheme.on('updated', fn);
} else { } else {
@ -265,7 +265,7 @@ export class Bridge {
} }
} }
restart(linuxSafeRestart = true) { public restart(linuxSafeRestart = true) {
// Note that in this case we are not sending the "appClose" event // Note that in this case we are not sending the "appClose" event
// to notify services and component that the app is about to close // to notify services and component that the app is about to close
// but for the current use-case it's not really needed. // but for the current use-case it's not really needed.

View File

@ -2,7 +2,6 @@ const React = require('react');
const { connect } = require('react-redux'); const { connect } = require('react-redux');
const { clipboard } = require('electron'); const { clipboard } = require('electron');
import ExtensionBadge from './ExtensionBadge'; import ExtensionBadge from './ExtensionBadge';
import bridge from '../services/bridge';
import { themeStyle } from '@joplin/lib/theme'; import { themeStyle } from '@joplin/lib/theme';
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import ClipperServer from '@joplin/lib/ClipperServer'; import ClipperServer from '@joplin/lib/ClipperServer';
@ -11,37 +10,29 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import { AppState } from '../app.reducer'; import { AppState } from '../app.reducer';
class ClipperConfigScreenComponent extends React.Component { class ClipperConfigScreenComponent extends React.Component {
constructor() { public constructor() {
super(); super();
this.copyToken_click = this.copyToken_click.bind(this); this.copyToken_click = this.copyToken_click.bind(this);
} }
disableClipperServer_click() { private disableClipperServer_click() {
Setting.setValue('clipperServer.autoStart', false); Setting.setValue('clipperServer.autoStart', false);
void ClipperServer.instance().stop(); void ClipperServer.instance().stop();
} }
enableClipperServer_click() { private enableClipperServer_click() {
Setting.setValue('clipperServer.autoStart', true); Setting.setValue('clipperServer.autoStart', true);
void ClipperServer.instance().start(); void ClipperServer.instance().start();
} }
chromeButton_click() { private copyToken_click() {
void bridge().openExternal('https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek');
}
firefoxButton_click() {
void bridge().openExternal('https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/');
}
copyToken_click() {
clipboard.writeText(this.props.apiToken); clipboard.writeText(this.props.apiToken);
alert(_('Token has been copied to the clipboard!')); alert(_('Token has been copied to the clipboard!'));
} }
renewToken_click() { private renewToken_click() {
if (confirm(_('Are you sure you want to renew the authorisation token?'))) { if (confirm(_('Are you sure you want to renew the authorisation token?'))) {
void EncryptionService.instance() void EncryptionService.instance()
.generateApiToken() .generateApiToken()
@ -52,7 +43,7 @@ class ClipperConfigScreenComponent extends React.Component {
} }
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const containerStyle = Object.assign({}, theme.containerStyle, { const containerStyle = Object.assign({}, theme.containerStyle, {

View File

@ -26,9 +26,9 @@ const settingKeyToControl: any = {
class ConfigScreenComponent extends React.Component<any, any> { class ConfigScreenComponent extends React.Component<any, any> {
rowStyle_: any = null; private rowStyle_: any = null;
constructor(props: any) { public constructor(props: any) {
super(props); super(props);
shared.init(this, reg); shared.init(this, reg);
@ -55,15 +55,15 @@ class ConfigScreenComponent extends React.Component<any, any> {
this.handleSettingButton = this.handleSettingButton.bind(this); this.handleSettingButton = this.handleSettingButton.bind(this);
} }
async checkSyncConfig_() { private async checkSyncConfig_() {
await shared.checkSyncConfig(this, this.state.settings); await shared.checkSyncConfig(this, this.state.settings);
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.setState({ settings: this.props.settings }); this.setState({ settings: this.props.settings });
} }
componentDidMount() { public componentDidMount() {
if (this.props.defaultSection) { if (this.props.defaultSection) {
this.setState({ selectedSectionName: this.props.defaultSection }, () => { this.setState({ selectedSectionName: this.props.defaultSection }, () => {
this.switchSection(this.props.defaultSection); this.switchSection(this.props.defaultSection);
@ -93,7 +93,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
} }
} }
sectionByName(name: string) { public sectionByName(name: string) {
const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings }); const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings });
for (const section of sections) { for (const section of sections) {
if (section.name === name) return section; if (section.name === name) return section;
@ -102,7 +102,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
throw new Error(`Invalid section name: ${name}`); throw new Error(`Invalid section name: ${name}`);
} }
screenFromName(screenName: string) { public screenFromName(screenName: string) {
if (screenName === 'encryption') return <EncryptionConfigScreen/>; if (screenName === 'encryption') return <EncryptionConfigScreen/>;
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>; if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>; if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
@ -110,7 +110,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
throw new Error(`Invalid screen name: ${screenName}`); throw new Error(`Invalid screen name: ${screenName}`);
} }
switchSection(name: string) { public switchSection(name: string) {
const section = this.sectionByName(name); const section = this.sectionByName(name);
let screenName = ''; let screenName = '';
if (section.isScreen) { if (section.isScreen) {
@ -125,11 +125,11 @@ class ConfigScreenComponent extends React.Component<any, any> {
this.setState({ selectedSectionName: section.name, screenName: screenName }); this.setState({ selectedSectionName: section.name, screenName: screenName });
} }
sidebar_selectionChange(event: any) { private sidebar_selectionChange(event: any) {
this.switchSection(event.section.name); this.switchSection(event.section.name);
} }
renderSectionDescription(section: any) { public renderSectionDescription(section: any) {
const description = Setting.sectionDescription(section.name); const description = Setting.sectionDescription(section.name);
if (!description) return null; if (!description) return null;
@ -141,7 +141,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
); );
} }
sectionToComponent(key: string, section: any, settings: any, selected: boolean) { public sectionToComponent(key: string, section: any, settings: any, selected: boolean) {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const createSettingComponents = (advanced: boolean) => { const createSettingComponents = (advanced: boolean) => {
@ -284,7 +284,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
return description ? <div style={this.descriptionStyle(themeId)}>{description}</div> : null; return description ? <div style={this.descriptionStyle(themeId)}>{description}</div> : null;
} }
settingToComponent(key: string, value: any) { public settingToComponent(key: string, value: any) {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const output: any = null; const output: any = null;
@ -657,26 +657,26 @@ class ConfigScreenComponent extends React.Component<any, any> {
} }
} }
async onApplyClick() { public async onApplyClick() {
shared.saveSettings(this); shared.saveSettings(this);
await this.checkNeedRestart(); await this.checkNeedRestart();
} }
async onSaveClick() { public async onSaveClick() {
shared.saveSettings(this); shared.saveSettings(this);
await this.checkNeedRestart(); await this.checkNeedRestart();
this.props.dispatch({ type: 'NAV_BACK' }); this.props.dispatch({ type: 'NAV_BACK' });
} }
onCancelClick() { public onCancelClick() {
this.props.dispatch({ type: 'NAV_BACK' }); this.props.dispatch({ type: 'NAV_BACK' });
} }
hasChanges() { public hasChanges() {
return !!this.state.changedSettingKeys.length; return !!this.state.changedSettingKeys.length;
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = Object.assign({}, const style = Object.assign({},

View File

@ -13,19 +13,19 @@ interface Props {
class DropboxLoginScreenComponent extends React.Component<any, any> { class DropboxLoginScreenComponent extends React.Component<any, any> {
shared_: any; private shared_: any;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.shared_ = new Shared(this, (msg: string) => bridge().showInfoMessageBox(msg), (msg: string) => bridge().showErrorMessageBox(msg)); this.shared_ = new Shared(this, (msg: string) => bridge().showInfoMessageBox(msg), (msg: string) => bridge().showErrorMessageBox(msg));
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.shared_.refreshUrl(); this.shared_.refreshUrl();
} }
render() { public render() {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);

View File

@ -32,7 +32,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
public state: State = { error: null, errorInfo: null, pluginInfos: [], plugins: {} }; public state: State = { error: null, errorInfo: null, pluginInfos: [], plugins: {} };
componentDidCatch(error: any, errorInfo: ErrorInfo) { public componentDidCatch(error: any, errorInfo: ErrorInfo) {
if (typeof error === 'string') error = { message: error }; if (typeof error === 'string') error = { message: error };
const pluginInfos: PluginInfo[] = []; const pluginInfos: PluginInfo[] = [];
@ -58,7 +58,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
this.setState({ error, errorInfo, pluginInfos, plugins }); this.setState({ error, errorInfo, pluginInfos, plugins });
} }
componentDidMount() { public componentDidMount() {
const onAppClose = () => { const onAppClose = () => {
ipcRenderer.send('asynchronous-message', 'appCloseReply', { ipcRenderer.send('asynchronous-message', 'appCloseReply', {
canClose: true, canClose: true,
@ -68,12 +68,12 @@ export default class ErrorBoundary extends React.Component<Props, State> {
ipcRenderer.on('appClose', onAppClose); ipcRenderer.on('appClose', onAppClose);
} }
renderMessage() { public renderMessage() {
const message = this.props.message || 'Joplin encountered a fatal error and could not continue.'; const message = this.props.message || 'Joplin encountered a fatal error and could not continue.';
return <p>{message}</p>; return <p>{message}</p>;
} }
render() { public render() {
if (this.state.error) { if (this.state.error) {
const safeMode_click = async () => { const safeMode_click = async () => {
Setting.setValue('isSafeMode', true); Setting.setValue('isSafeMode', true);

View File

@ -11,17 +11,17 @@ interface Props {
} }
class HelpButtonComponent extends React.Component<Props> { class HelpButtonComponent extends React.Component<Props> {
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.onClick = this.onClick.bind(this); this.onClick = this.onClick.bind(this);
} }
onClick() { public onClick() {
if (this.props.onClick) this.props.onClick(); if (this.props.onClick) this.props.onClick();
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' }); const style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' });
const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 }; const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 };

View File

@ -9,7 +9,7 @@ interface Props {
} }
class IconButton extends React.Component<Props> { class IconButton extends React.Component<Props> {
render() { public render() {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const iconStyle = { const iconStyle = {

View File

@ -24,7 +24,7 @@ interface State {
} }
class ImportScreenComponent extends React.Component<Props, State> { class ImportScreenComponent extends React.Component<Props, State> {
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.setState({ this.setState({
doImport: true, doImport: true,
filePath: this.props.filePath, filePath: this.props.filePath,
@ -32,7 +32,7 @@ class ImportScreenComponent extends React.Component<Props, State> {
}); });
} }
UNSAFE_componentWillReceiveProps(newProps: Props) { public UNSAFE_componentWillReceiveProps(newProps: Props) {
if (newProps.filePath) { if (newProps.filePath) {
this.setState( this.setState(
{ {
@ -47,13 +47,13 @@ class ImportScreenComponent extends React.Component<Props, State> {
} }
} }
componentDidMount() { public componentDidMount() {
if (this.state.filePath && this.state.doImport) { if (this.state.filePath && this.state.doImport) {
void this.doImport(); void this.doImport();
} }
} }
addMessage(key: string, text: string) { public addMessage(key: string, text: string) {
const messages = this.state.messages.slice(); const messages = this.state.messages.slice();
messages.push({ key: key, text: text }); messages.push({ key: key, text: text });
@ -61,7 +61,7 @@ class ImportScreenComponent extends React.Component<Props, State> {
this.setState({ messages: messages }); this.setState({ messages: messages });
} }
uniqueMessages() { public uniqueMessages() {
const output = []; const output = [];
const messages = this.state.messages.slice(); const messages = this.state.messages.slice();
const foundKeys = []; const foundKeys = [];
@ -74,7 +74,7 @@ class ImportScreenComponent extends React.Component<Props, State> {
return output; return output;
} }
async doImport() { public async doImport() {
const filePath = this.props.filePath; const filePath = this.props.filePath;
const folderTitle = await Folder.findUniqueItemTitle(filename(filePath)); const folderTitle = await Folder.findUniqueItemTitle(filename(filePath));
@ -109,7 +109,7 @@ class ImportScreenComponent extends React.Component<Props, State> {
this.setState({ doImport: false }); this.setState({ doImport: false });
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const messages = this.uniqueMessages(); const messages = this.uniqueMessages();

View File

@ -21,7 +21,7 @@ class ItemList extends React.Component<Props, State> {
private scrollTop_: number; private scrollTop_: number;
private listRef: any; private listRef: any;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.scrollTop_ = 0; this.scrollTop_ = 0;
@ -33,12 +33,12 @@ class ItemList extends React.Component<Props, State> {
this.onDrop = this.onDrop.bind(this); this.onDrop = this.onDrop.bind(this);
} }
visibleItemCount(props: Props = undefined) { public visibleItemCount(props: Props = undefined) {
if (typeof props === 'undefined') props = this.props; if (typeof props === 'undefined') props = this.props;
return Math.ceil(props.style.height / props.itemHeight); return Math.ceil(props.style.height / props.itemHeight);
} }
updateStateItemIndexes(props: Props = undefined) { public updateStateItemIndexes(props: Props = undefined) {
if (typeof props === 'undefined') props = this.props; if (typeof props === 'undefined') props = this.props;
const topItemIndex = Math.floor(this.scrollTop_ / props.itemHeight); const topItemIndex = Math.floor(this.scrollTop_ / props.itemHeight);
@ -53,36 +53,36 @@ class ItemList extends React.Component<Props, State> {
}); });
} }
offsetTop() { public offsetTop() {
return this.listRef.current ? this.listRef.current.offsetTop : 0; return this.listRef.current ? this.listRef.current.offsetTop : 0;
} }
offsetScroll() { public offsetScroll() {
return this.scrollTop_; return this.scrollTop_;
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.updateStateItemIndexes(); this.updateStateItemIndexes();
} }
UNSAFE_componentWillReceiveProps(newProps: Props) { public UNSAFE_componentWillReceiveProps(newProps: Props) {
this.updateStateItemIndexes(newProps); this.updateStateItemIndexes(newProps);
} }
onScroll(event: any) { public onScroll(event: any) {
this.scrollTop_ = event.target.scrollTop; this.scrollTop_ = event.target.scrollTop;
this.updateStateItemIndexes(); this.updateStateItemIndexes();
} }
onKeyDown(event: any) { public onKeyDown(event: any) {
if (this.props.onKeyDown) this.props.onKeyDown(event); if (this.props.onKeyDown) this.props.onKeyDown(event);
} }
onDrop(event: any) { public onDrop(event: any) {
if (this.props.onNoteDrop) this.props.onNoteDrop(event); if (this.props.onNoteDrop) this.props.onNoteDrop(event);
} }
makeItemIndexVisible(itemIndex: number) { public makeItemIndexVisible(itemIndex: number) {
const top = Math.min(this.props.items.length - 1, this.state.topItemIndex); const top = Math.min(this.props.items.length - 1, this.state.topItemIndex);
const bottom = Math.max(0, this.state.bottomItemIndex); const bottom = Math.max(0, this.state.bottomItemIndex);
@ -119,7 +119,7 @@ class ItemList extends React.Component<Props, State> {
// return true; // return true;
// } // }
render() { public render() {
const items = this.props.items; const items = this.props.items;
const style = Object.assign({}, this.props.style, { const style = Object.assign({}, this.props.style, {
overflowX: 'hidden', overflowX: 'hidden',

View File

@ -123,7 +123,7 @@ class MainScreenComponent extends React.Component<Props, State> {
private styles_: any; private styles_: any;
private promptOnClose_: Function; private promptOnClose_: Function;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
@ -250,11 +250,11 @@ class MainScreenComponent extends React.Component<Props, State> {
return this.updateLayoutPluginViews(output, plugins); return this.updateLayoutPluginViews(output, plugins);
} }
window_resize() { private window_resize() {
this.updateRootLayoutSize(); this.updateRootLayoutSize();
} }
setupAppCloseHandling() { public setupAppCloseHandling() {
this.waitForNotesSavedIID_ = null; this.waitForNotesSavedIID_ = null;
// This event is dispached from the main process when the app is about // This event is dispached from the main process when the app is about
@ -289,11 +289,11 @@ class MainScreenComponent extends React.Component<Props, State> {
}); });
} }
notePropertiesDialog_close() { private notePropertiesDialog_close() {
this.setState({ notePropertiesDialogOptions: {} }); this.setState({ notePropertiesDialogOptions: {} });
} }
noteContentPropertiesDialog_close() { private noteContentPropertiesDialog_close() {
this.setState({ noteContentPropertiesDialogOptions: {} }); this.setState({ noteContentPropertiesDialogOptions: {} });
} }
@ -305,14 +305,14 @@ class MainScreenComponent extends React.Component<Props, State> {
this.setState({ shareFolderDialogOptions: { visible: false, folderId: '' } }); this.setState({ shareFolderDialogOptions: { visible: false, folderId: '' } });
} }
updateMainLayout(layout: LayoutItem) { public updateMainLayout(layout: LayoutItem) {
this.props.dispatch({ this.props.dispatch({
type: 'MAIN_LAYOUT_SET', type: 'MAIN_LAYOUT_SET',
value: layout, value: layout,
}); });
} }
updateRootLayoutSize() { public updateRootLayoutSize() {
this.updateMainLayout(produce(this.props.mainLayout, (draft: any) => { this.updateMainLayout(produce(this.props.mainLayout, (draft: any) => {
const s = this.rootLayoutSize(); const s = this.rootLayoutSize();
draft.width = s.width; draft.width = s.width;
@ -320,7 +320,7 @@ class MainScreenComponent extends React.Component<Props, State> {
})); }));
} }
componentDidUpdate(prevProps: Props, prevState: State) { public componentDidUpdate(prevProps: Props, prevState: State) {
if (prevProps.style.width !== this.props.style.width || if (prevProps.style.width !== this.props.style.width ||
prevProps.style.height !== this.props.style.height || prevProps.style.height !== this.props.style.height ||
this.messageBoxVisible(prevProps) !== this.messageBoxVisible(this.props) this.messageBoxVisible(prevProps) !== this.messageBoxVisible(this.props)
@ -383,24 +383,24 @@ class MainScreenComponent extends React.Component<Props, State> {
} }
} }
layoutModeListenerKeyDown(event: any) { public layoutModeListenerKeyDown(event: any) {
if (event.key !== 'Escape') return; if (event.key !== 'Escape') return;
if (!this.props.layoutMoveMode) return; if (!this.props.layoutMoveMode) return;
void CommandService.instance().execute('toggleLayoutMoveMode'); void CommandService.instance().execute('toggleLayoutMoveMode');
} }
componentDidMount() { public componentDidMount() {
window.addEventListener('keydown', this.layoutModeListenerKeyDown); window.addEventListener('keydown', this.layoutModeListenerKeyDown);
} }
componentWillUnmount() { public componentWillUnmount() {
this.unregisterCommands(); this.unregisterCommands();
window.removeEventListener('resize', this.window_resize); window.removeEventListener('resize', this.window_resize);
window.removeEventListener('keydown', this.layoutModeListenerKeyDown); window.removeEventListener('keydown', this.layoutModeListenerKeyDown);
} }
async waitForNoteToSaved(noteId: string) { public async waitForNoteToSaved(noteId: string) {
while (noteId && this.props.editorNoteStatuses[noteId] === 'saving') { while (noteId && this.props.editorNoteStatuses[noteId] === 'saving') {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.info('Waiting for note to be saved...', this.props.editorNoteStatuses); console.info('Waiting for note to be saved...', this.props.editorNoteStatuses);
@ -408,7 +408,7 @@ class MainScreenComponent extends React.Component<Props, State> {
} }
} }
async printTo_(target: string, options: any) { public async printTo_(target: string, options: any) {
// Concurrent print calls are disallowed to avoid incorrect settings being restored upon completion // Concurrent print calls are disallowed to avoid incorrect settings being restored upon completion
if (this.isPrinting_) { if (this.isPrinting_) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -449,23 +449,23 @@ class MainScreenComponent extends React.Component<Props, State> {
this.isPrinting_ = false; this.isPrinting_ = false;
} }
rootLayoutSize() { public rootLayoutSize() {
return { return {
width: window.innerWidth, width: window.innerWidth,
height: this.rowHeight(), height: this.rowHeight(),
}; };
} }
rowHeight() { public rowHeight() {
if (!this.props) return 0; if (!this.props) return 0;
return this.props.style.height - (this.messageBoxVisible() ? this.messageBoxHeight() : 0); return this.props.style.height - (this.messageBoxVisible() ? this.messageBoxHeight() : 0);
} }
messageBoxHeight() { public messageBoxHeight() {
return 50; return 50;
} }
styles(themeId: number, width: number, height: number, messageBoxVisible: boolean) { public styles(themeId: number, width: number, height: number, messageBoxVisible: boolean) {
const styleKey = [themeId, width, height, messageBoxVisible].join('_'); const styleKey = [themeId, width, height, messageBoxVisible].join('_');
if (styleKey === this.styleKey_) return this.styles_; if (styleKey === this.styleKey_) return this.styles_;
@ -539,7 +539,7 @@ class MainScreenComponent extends React.Component<Props, State> {
); );
} }
renderNotification(theme: any, styles: any) { public renderNotification(theme: any, styles: any) {
if (!this.messageBoxVisible()) return null; if (!this.messageBoxVisible()) return null;
const onViewStatusScreen = () => { const onViewStatusScreen = () => {
@ -658,33 +658,33 @@ class MainScreenComponent extends React.Component<Props, State> {
); );
} }
messageBoxVisible(props: Props = null) { public messageBoxVisible(props: Props = null) {
if (!props) props = this.props; if (!props) props = this.props;
return props.hasDisabledSyncItems || props.showMissingMasterKeyMessage || props.showNeedUpgradingMasterKeyMessage || props.showShouldReencryptMessage || props.hasDisabledEncryptionItems || this.props.shouldUpgradeSyncTarget || props.isSafeMode || this.showShareInvitationNotification(props) || this.props.needApiAuth || this.props.showInstallTemplatesPlugin; return props.hasDisabledSyncItems || props.showMissingMasterKeyMessage || props.showNeedUpgradingMasterKeyMessage || props.showShouldReencryptMessage || props.hasDisabledEncryptionItems || this.props.shouldUpgradeSyncTarget || props.isSafeMode || this.showShareInvitationNotification(props) || this.props.needApiAuth || this.props.showInstallTemplatesPlugin;
} }
registerCommands() { public registerCommands() {
for (const command of commands) { for (const command of commands) {
CommandService.instance().registerRuntime(command.declaration.name, command.runtime(this)); CommandService.instance().registerRuntime(command.declaration.name, command.runtime(this));
} }
} }
unregisterCommands() { public unregisterCommands() {
for (const command of commands) { for (const command of commands) {
CommandService.instance().unregisterRuntime(command.declaration.name); CommandService.instance().unregisterRuntime(command.declaration.name);
} }
} }
resizableLayout_resize(event: any) { private resizableLayout_resize(event: any) {
this.updateMainLayout(event.layout); this.updateMainLayout(event.layout);
} }
resizableLayout_moveButtonClick(event: MoveButtonClickEvent) { private resizableLayout_moveButtonClick(event: MoveButtonClickEvent) {
const newLayout = move(this.props.mainLayout, event.itemKey, event.direction); const newLayout = move(this.props.mainLayout, event.itemKey, event.direction);
this.updateMainLayout(newLayout); this.updateMainLayout(newLayout);
} }
resizableLayout_renderItem(key: string, event: any) { private resizableLayout_renderItem(key: string, event: any) {
// Key should never be undefined but somehow it can happen, also not // Key should never be undefined but somehow it can happen, also not
// clear how. For now in this case render nothing so that the app // clear how. For now in this case render nothing so that the app
// doesn't crash. // doesn't crash.
@ -770,7 +770,7 @@ class MainScreenComponent extends React.Component<Props, State> {
} }
} }
renderPluginDialogs() { public renderPluginDialogs() {
const output = []; const output = [];
const infos = pluginUtils.viewInfosByType(this.props.plugins, 'webview'); const infos = pluginUtils.viewInfosByType(this.props.plugins, 'webview');
@ -801,7 +801,7 @@ class MainScreenComponent extends React.Component<Props, State> {
); );
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = Object.assign( const style = Object.assign(
{ {

View File

@ -9,7 +9,7 @@ interface Props {
} }
class NavigatorComponent extends React.Component<Props> { class NavigatorComponent extends React.Component<Props> {
UNSAFE_componentWillReceiveProps(newProps: Props) { public UNSAFE_componentWillReceiveProps(newProps: Props) {
if (newProps.route) { if (newProps.route) {
const screenInfo = this.props.screens[newProps.route.routeName]; const screenInfo = this.props.screens[newProps.route.routeName];
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : ''; const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
@ -21,7 +21,7 @@ class NavigatorComponent extends React.Component<Props> {
} }
} }
updateWindowTitle(title: string) { public updateWindowTitle(title: string) {
try { try {
if (bridge().window()) bridge().window().setTitle(title); if (bridge().window()) bridge().window().setTitle(title);
} catch (error) { } catch (error) {
@ -29,7 +29,7 @@ class NavigatorComponent extends React.Component<Props> {
} }
} }
render() { public render() {
if (!this.props.route) throw new Error('Route must not be null'); if (!this.props.route) throw new Error('Route must not be null');
const route = this.props.route; const route = this.props.route;

View File

@ -31,7 +31,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
private styleKey_: number; private styleKey_: number;
private styles_: any; private styles_: any;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.revisionsLink_click = this.revisionsLink_click.bind(this); this.revisionsLink_click = this.revisionsLink_click.bind(this);
@ -56,17 +56,17 @@ class NotePropertiesDialog extends React.Component<Props, State> {
}; };
} }
componentDidMount() { public componentDidMount() {
void this.loadNote(this.props.noteId); void this.loadNote(this.props.noteId);
} }
componentDidUpdate() { public componentDidUpdate() {
if (this.state.editedKey === null) { if (this.state.editedKey === null) {
this.okButton.current.focus(); this.okButton.current.focus();
} }
} }
async loadNote(noteId: string) { public async loadNote(noteId: string) {
if (!noteId) { if (!noteId) {
this.setState({ formNote: null }); this.setState({ formNote: null });
} else { } else {
@ -76,7 +76,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
} }
} }
latLongFromLocation(location: string) { public latLongFromLocation(location: string) {
const o: any = {}; const o: any = {};
const l = location.split(','); const l = location.split(',');
if (l.length === 2) { if (l.length === 2) {
@ -89,7 +89,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return o; return o;
} }
noteToFormNote(note: NoteEntity) { public noteToFormNote(note: NoteEntity) {
const formNote: any = {}; const formNote: any = {};
formNote.user_updated_time = time.formatMsToLocal(note.user_updated_time); formNote.user_updated_time = time.formatMsToLocal(note.user_updated_time);
@ -113,7 +113,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return formNote; return formNote;
} }
formNoteToNote(formNote: any) { public formNoteToNote(formNote: any) {
const note = Object.assign({ id: formNote.id }, this.latLongFromLocation(formNote.location)); const note = Object.assign({ id: formNote.id }, this.latLongFromLocation(formNote.location));
note.user_created_time = time.formatLocalToMs(formNote.user_created_time); note.user_created_time = time.formatLocalToMs(formNote.user_created_time);
note.user_updated_time = time.formatLocalToMs(formNote.user_updated_time); note.user_updated_time = time.formatLocalToMs(formNote.user_updated_time);
@ -127,7 +127,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return note; return note;
} }
styles(themeId: number) { public styles(themeId: number) {
const styleKey = themeId; const styleKey = themeId;
if (styleKey === this.styleKey_) return this.styles_; if (styleKey === this.styleKey_) return this.styles_;
@ -168,7 +168,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return this.styles_; return this.styles_;
} }
async closeDialog(applyChanges: boolean) { public async closeDialog(applyChanges: boolean) {
if (applyChanges) { if (applyChanges) {
await this.saveProperty(); await this.saveProperty();
const note = this.formNoteToNote(this.state.formNote); const note = this.formNoteToNote(this.state.formNote);
@ -183,16 +183,16 @@ class NotePropertiesDialog extends React.Component<Props, State> {
} }
} }
buttonRow_click(event: any) { private buttonRow_click(event: any) {
void this.closeDialog(event.buttonName === 'ok'); void this.closeDialog(event.buttonName === 'ok');
} }
revisionsLink_click() { private revisionsLink_click() {
void this.closeDialog(false); void this.closeDialog(false);
if (this.props.onRevisionLinkClick) this.props.onRevisionLinkClick(); if (this.props.onRevisionLinkClick) this.props.onRevisionLinkClick();
} }
editPropertyButtonClick(key: string, initialValue: any) { public editPropertyButtonClick(key: string, initialValue: any) {
this.setState({ this.setState({
editedKey: key, editedKey: key,
editedValue: initialValue, editedValue: initialValue,
@ -207,7 +207,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
}, 100); }, 100);
} }
async saveProperty() { public async saveProperty() {
if (!this.state.editedKey) return; if (!this.state.editedKey) return;
return new Promise((resolve: Function) => { return new Promise((resolve: Function) => {
@ -233,7 +233,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
}); });
} }
async cancelProperty() { public async cancelProperty() {
return new Promise((resolve: Function) => { return new Promise((resolve: Function) => {
this.okButton.current.focus(); this.okButton.current.focus();
this.setState({ this.setState({
@ -245,7 +245,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
}); });
} }
createNoteField(key: string, value: any) { public createNoteField(key: string, value: any) {
const styles = this.styles(this.props.themeId); const styles = this.styles(this.props.themeId);
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const labelComp = <label style={Object.assign({}, theme.textStyle, theme.controlBoxLabel)}>{this.formatLabel(key)}</label>; const labelComp = <label style={Object.assign({}, theme.textStyle, theme.controlBoxLabel)}>{this.formatLabel(key)}</label>;
@ -364,12 +364,12 @@ class NotePropertiesDialog extends React.Component<Props, State> {
); );
} }
formatLabel(key: string) { public formatLabel(key: string) {
if (this.keyToLabel_[key]) return this.keyToLabel_[key]; if (this.keyToLabel_[key]) return this.keyToLabel_[key];
return key; return key;
} }
formatValue(key: string, note: NoteEntity) { public formatValue(key: string, note: NoteEntity) {
if (key === 'location') { if (key === 'location') {
if (!Number(note.latitude) && !Number(note.longitude)) return null; if (!Number(note.latitude) && !Number(note.longitude)) return null;
const dms = formatcoords(Number(note.latitude), Number(note.longitude)); const dms = formatcoords(Number(note.latitude), Number(note.longitude));
@ -383,7 +383,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return (note as any)[key]; return (note as any)[key];
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const formNote = this.state.formNote; const formNote = this.state.formNote;

View File

@ -38,7 +38,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
private viewerRef_: any; private viewerRef_: any;
private helpButton_onClick: Function; private helpButton_onClick: Function;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
@ -57,7 +57,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
this.webview_ipcMessage = this.webview_ipcMessage.bind(this); this.webview_ipcMessage = this.webview_ipcMessage.bind(this);
} }
style() { public style() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = { const style = {
@ -74,7 +74,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
return style; return style;
} }
async viewer_domReady() { private async viewer_domReady() {
// this.viewerRef_.current.openDevTools(); // this.viewerRef_.current.openDevTools();
const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, this.props.noteId); const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, this.props.noteId);
@ -90,7 +90,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
); );
} }
async importButton_onClick() { private async importButton_onClick() {
if (!this.state.note) return; if (!this.state.note) return;
this.setState({ restoring: true }); this.setState({ restoring: true });
await RevisionService.instance().importRevisionNote(this.state.note); await RevisionService.instance().importRevisionNote(this.state.note);
@ -98,11 +98,11 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
alert(RevisionService.instance().restoreSuccessMessage(this.state.note)); alert(RevisionService.instance().restoreSuccessMessage(this.state.note));
} }
backButton_click() { private backButton_click() {
if (this.props.onBack) this.props.onBack(); if (this.props.onBack) this.props.onBack();
} }
revisionList_onChange(event: any) { private revisionList_onChange(event: any) {
const value = event.target.value; const value = event.target.value;
if (!value) { if (!value) {
@ -119,7 +119,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
} }
} }
async reloadNote() { public async reloadNote() {
let noteBody = ''; let noteBody = '';
let markupLanguage = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN; let markupLanguage = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
if (!this.state.revisions.length || !this.state.currentRevId) { if (!this.state.revisions.length || !this.state.currentRevId) {
@ -153,7 +153,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
}); });
} }
async webview_ipcMessage(event: any) { private async webview_ipcMessage(event: any) {
// For the revision view, we only suppport a minimal subset of the IPC messages. // For the revision view, we only suppport a minimal subset of the IPC messages.
// For example, we don't need interactive checkboxes or sync between viewer and editor view. // For example, we don't need interactive checkboxes or sync between viewer and editor view.
// We try to get most links work though, except for internal (joplin://) links. // We try to get most links work though, except for internal (joplin://) links.
@ -183,7 +183,7 @@ class NoteRevisionViewerComponent extends React.PureComponent<Props, State> {
} }
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = this.style(); const style = this.style();

View File

@ -20,7 +20,7 @@ class NoteSearchBar extends React.Component<Props> {
private backgroundColor: any; private backgroundColor: any;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.searchInput_change = this.searchInput_change.bind(this); this.searchInput_change = this.searchInput_change.bind(this);
@ -33,7 +33,7 @@ class NoteSearchBar extends React.Component<Props> {
this.backgroundColor = undefined; this.backgroundColor = undefined;
} }
style() { public style() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = { const style = {
@ -46,7 +46,7 @@ class NoteSearchBar extends React.Component<Props> {
return style; return style;
} }
buttonIconComponent(iconName: string, clickHandler: any, isEnabled: boolean) { public buttonIconComponent(iconName: string, clickHandler: any, isEnabled: boolean) {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const searchButton = { const searchButton = {
@ -74,12 +74,12 @@ class NoteSearchBar extends React.Component<Props> {
); );
} }
searchInput_change(event: any) { private searchInput_change(event: any) {
const query = event.currentTarget.value; const query = event.currentTarget.value;
this.triggerOnChange(query); this.triggerOnChange(query);
} }
searchInput_keyDown(event: any) { private searchInput_keyDown(event: any) {
if (event.keyCode === 13) { if (event.keyCode === 13) {
// ENTER // ENTER
event.preventDefault(); event.preventDefault();
@ -106,28 +106,28 @@ class NoteSearchBar extends React.Component<Props> {
} }
} }
previousButton_click() { private previousButton_click() {
if (this.props.onPrevious) this.props.onPrevious(); if (this.props.onPrevious) this.props.onPrevious();
} }
nextButton_click() { private nextButton_click() {
if (this.props.onNext) this.props.onNext(); if (this.props.onNext) this.props.onNext();
} }
closeButton_click() { private closeButton_click() {
if (this.props.onClose) this.props.onClose(); if (this.props.onClose) this.props.onClose();
} }
triggerOnChange(query: string) { public triggerOnChange(query: string) {
if (this.props.onChange) this.props.onChange(query); if (this.props.onChange) this.props.onChange(query);
} }
focus() { public focus() {
(this.refs.searchInput as any).focus(); (this.refs.searchInput as any).focus();
(this.refs.searchInput as any).select(); (this.refs.searchInput as any).select();
} }
render() { public render() {
const query = this.props.query ? this.props.query : ''; const query = this.props.query ? this.props.query : '';
// backgroundColor needs to cached to a local variable to prevent the // backgroundColor needs to cached to a local variable to prevent the

View File

@ -11,7 +11,7 @@ interface Props {
} }
class NoteStatusBarComponent extends React.Component<Props> { class NoteStatusBarComponent extends React.Component<Props> {
style() { public style() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = { const style = {
@ -24,7 +24,7 @@ class NoteStatusBarComponent extends React.Component<Props> {
return style; return style;
} }
render() { public render() {
const note = this.props.note; const note = this.props.note;
return <div style={this.style().root}>{time.formatMsToLocal(note.user_updated_time)}</div>; return <div style={this.style().root}>{time.formatMsToLocal(note.user_updated_time)}</div>;
} }

View File

@ -17,7 +17,7 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
private webviewRef_: any; private webviewRef_: any;
private webviewListeners_: any = null; private webviewListeners_: any = null;
constructor(props: any) { public constructor(props: any) {
super(props); super(props);
this.webviewRef_ = React.createRef(); this.webviewRef_ = React.createRef();
@ -41,20 +41,20 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
this.webview_message = this.webview_message.bind(this); this.webview_message = this.webview_message.bind(this);
} }
webview_domReady(event: any) { private webview_domReady(event: any) {
this.domReady_ = true; this.domReady_ = true;
if (this.props.onDomReady) this.props.onDomReady(event); if (this.props.onDomReady) this.props.onDomReady(event);
} }
webview_ipcMessage(event: any) { private webview_ipcMessage(event: any) {
if (this.props.onIpcMessage) this.props.onIpcMessage(event); if (this.props.onIpcMessage) this.props.onIpcMessage(event);
} }
webview_load() { private webview_load() {
this.webview_domReady({}); this.webview_domReady({});
} }
webview_message(event: any) { private webview_message(event: any) {
if (!event.data || event.data.target !== 'main') return; if (!event.data || event.data.target !== 'main') return;
const callName = event.data.name; const callName = event.data.name;
@ -68,11 +68,11 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
} }
} }
domReady() { public domReady() {
return this.domReady_; return this.domReady_;
} }
initWebview() { public initWebview() {
const wv = this.webviewRef_.current; const wv = this.webviewRef_.current;
if (!this.webviewListeners_) { if (!this.webviewListeners_) {
@ -92,7 +92,7 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
this.webviewRef_.current.contentWindow.addEventListener('message', this.webview_message); this.webviewRef_.current.contentWindow.addEventListener('message', this.webview_message);
} }
destroyWebview() { public destroyWebview() {
const wv = this.webviewRef_.current; const wv = this.webviewRef_.current;
if (!wv || !this.initialized_) return; if (!wv || !this.initialized_) return;
@ -115,28 +115,28 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
this.domReady_ = false; this.domReady_ = false;
} }
focus() { public focus() {
if (this.webviewRef_.current) { if (this.webviewRef_.current) {
this.webviewRef_.current.focus(); this.webviewRef_.current.focus();
} }
} }
tryInit() { public tryInit() {
if (!this.initialized_ && this.webviewRef_.current) { if (!this.initialized_ && this.webviewRef_.current) {
this.initWebview(); this.initWebview();
this.initialized_ = true; this.initialized_ = true;
} }
} }
componentDidMount() { public componentDidMount() {
this.tryInit(); this.tryInit();
} }
componentDidUpdate() { public componentDidUpdate() {
this.tryInit(); this.tryInit();
} }
componentWillUnmount() { public componentWillUnmount() {
this.destroyWebview(); this.destroyWebview();
} }
@ -144,7 +144,7 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
// Wrap WebView functions // Wrap WebView functions
// ---------------------------------------------------------------- // ----------------------------------------------------------------
send(channel: string, arg0: any = null, arg1: any = null) { public send(channel: string, arg0: any = null, arg1: any = null) {
const win = this.webviewRef_.current.contentWindow; const win = this.webviewRef_.current.contentWindow;
if (channel === 'focus') { if (channel === 'focus') {
@ -172,7 +172,7 @@ export default class NoteTextViewerComponent extends React.Component<Props, any>
// Wrap WebView functions (END) // Wrap WebView functions (END)
// ---------------------------------------------------------------- // ----------------------------------------------------------------
render() { public render() {
const viewerStyle = Object.assign({}, { border: 'none' }, this.props.viewerStyle); const viewerStyle = Object.assign({}, { border: 'none' }, this.props.viewerStyle);
return <iframe className="noteTextViewer" ref={this.webviewRef_} style={viewerStyle} src="gui/note-viewer/index.html"></iframe>; return <iframe className="noteTextViewer" ref={this.webviewRef_} style={viewerStyle} src="gui/note-viewer/index.html"></iframe>;
} }

View File

@ -14,7 +14,7 @@ interface Props {
} }
class OneDriveLoginScreenComponent extends React.Component<any, any> { class OneDriveLoginScreenComponent extends React.Component<any, any> {
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
@ -22,7 +22,7 @@ class OneDriveLoginScreenComponent extends React.Component<any, any> {
}; };
} }
async componentDidMount() { public async componentDidMount() {
const log = (s: any) => { const log = (s: any) => {
this.setState((state: any) => { this.setState((state: any) => {
const authLog = state.authLog.slice(); const authLog = state.authLog.slice();
@ -48,15 +48,15 @@ class OneDriveLoginScreenComponent extends React.Component<any, any> {
} }
} }
startUrl() { public startUrl() {
return reg.syncTarget().api().authCodeUrl(this.redirectUrl()); return reg.syncTarget().api().authCodeUrl(this.redirectUrl());
} }
redirectUrl() { public redirectUrl() {
return reg.syncTarget().api().nativeClientRedirectUrl(); return reg.syncTarget().api().nativeClientRedirectUrl();
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const logComps = []; const logComps = [];

View File

@ -27,13 +27,13 @@ export default class PromptDialog extends React.Component<Props, any> {
private styles_: any; private styles_: any;
private styleKey_: string; private styleKey_: string;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.answerInput_ = React.createRef(); this.answerInput_ = React.createRef();
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.setState({ this.setState({
visible: false, visible: false,
answer: this.props.defaultValue ? this.props.defaultValue : '', answer: this.props.defaultValue ? this.props.defaultValue : '',
@ -41,7 +41,7 @@ export default class PromptDialog extends React.Component<Props, any> {
this.focusInput_ = true; this.focusInput_ = true;
} }
UNSAFE_componentWillReceiveProps(newProps: Props) { public UNSAFE_componentWillReceiveProps(newProps: Props) {
if ('visible' in newProps && newProps.visible !== this.props.visible) { if ('visible' in newProps && newProps.visible !== this.props.visible) {
this.setState({ visible: newProps.visible }); this.setState({ visible: newProps.visible });
if (newProps.visible) this.focusInput_ = true; if (newProps.visible) this.focusInput_ = true;
@ -52,12 +52,12 @@ export default class PromptDialog extends React.Component<Props, any> {
} }
} }
componentDidUpdate() { public componentDidUpdate() {
if (this.focusInput_ && this.answerInput_.current) this.answerInput_.current.focus(); if (this.focusInput_ && this.answerInput_.current) this.answerInput_.current.focus();
this.focusInput_ = false; this.focusInput_ = false;
} }
styles(themeId: number, width: number, height: number, visible: boolean) { public styles(themeId: number, width: number, height: number, visible: boolean) {
const styleKey = `${themeId}_${width}_${height}_${visible}`; const styleKey = `${themeId}_${width}_${height}_${visible}`;
if (styleKey === this.styleKey_) return this.styles_; if (styleKey === this.styleKey_) return this.styles_;
@ -181,7 +181,7 @@ export default class PromptDialog extends React.Component<Props, any> {
return this.styles_; return this.styles_;
} }
render() { public render() {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const buttonTypes = this.props.buttons ? this.props.buttons : ['ok', 'cancel']; const buttonTypes = this.props.buttons ? this.props.buttons : ['ok', 'cancel'];

View File

@ -135,7 +135,7 @@ const getNextSortingOrderType = (s: SortingType): SortingType => {
const MAX_RESOURCES = 10000; const MAX_RESOURCES = 10000;
class ResourceScreenComponent extends React.Component<Props, State> { class ResourceScreenComponent extends React.Component<Props, State> {
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
resources: undefined, resources: undefined,
@ -147,7 +147,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
}; };
} }
async reloadResources(sorting: ActiveSorting) { public async reloadResources(sorting: ActiveSorting) {
this.setState({ isLoading: true }); this.setState({ isLoading: true });
const resources = await Resource.all({ const resources = await Resource.all({
order: [{ order: [{
@ -161,11 +161,11 @@ class ResourceScreenComponent extends React.Component<Props, State> {
this.setState({ resources, isLoading: false }); this.setState({ resources, isLoading: false });
} }
componentDidMount() { public componentDidMount() {
void this.reloadResources(this.state.sorting); void this.reloadResources(this.state.sorting);
} }
onResourceDelete(resource: InnerResource) { public onResourceDelete(resource: InnerResource) {
const ok = bridge().showConfirmMessageBox(_('Delete attachment "%s"?', resource.title), { const ok = bridge().showConfirmMessageBox(_('Delete attachment "%s"?', resource.title), {
buttons: [_('Delete'), _('Cancel')], buttons: [_('Delete'), _('Cancel')],
defaultId: 1, defaultId: 1,
@ -184,7 +184,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
}); });
} }
openResource(resource: InnerResource) { public openResource(resource: InnerResource) {
const resourcePath = Resource.fullPath(resource); const resourcePath = Resource.fullPath(resource);
const ok = bridge().openExternal(`file://${resourcePath}`); const ok = bridge().openExternal(`file://${resourcePath}`);
if (!ok) { if (!ok) {
@ -192,7 +192,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
} }
} }
onToggleSortOrder(sortOrder: SortingOrder) { public onToggleSortOrder(sortOrder: SortingOrder) {
let newSorting = { ...this.state.sorting }; let newSorting = { ...this.state.sorting };
if (sortOrder === this.state.sorting.order) { if (sortOrder === this.state.sorting.order) {
newSorting.type = getNextSortingOrderType(newSorting.type); newSorting.type = getNextSortingOrderType(newSorting.type);
@ -206,7 +206,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
void this.reloadResources(newSorting); void this.reloadResources(newSorting);
} }
render() { public render() {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);

View File

@ -5,7 +5,7 @@ import CommandService from '@joplin/lib/services/CommandService';
import { AppState } from '../app.reducer'; import { AppState } from '../app.reducer';
class TagItemComponent extends React.Component { class TagItemComponent extends React.Component {
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = Object.assign({}, theme.tagStyle); const style = Object.assign({}, theme.tagStyle);
const { title, id } = this.props; const { title, id } = this.props;

View File

@ -13,7 +13,7 @@ interface Props {
class ToolbarBaseComponent extends React.Component<Props, any> { class ToolbarBaseComponent extends React.Component<Props, any> {
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style: any = Object.assign({ const style: any = Object.assign({

View File

@ -6,7 +6,7 @@ interface Props {
} }
class ToolbarSpace extends React.Component<Props> { class ToolbarSpace extends React.Component<Props> {
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = Object.assign({}, theme.toolbarStyle); const style = Object.assign({}, theme.toolbarStyle);
style.minWidth = style.height / 2; style.minWidth = style.height / 2;

View File

@ -7,11 +7,11 @@ const smalltalk = require('smalltalk');
const logger = Logger.create('dialogs'); const logger = Logger.create('dialogs');
class Dialogs { class Dialogs {
async alert(message: string, title = '') { public async alert(message: string, title = '') {
await smalltalk.alert(title, message); await smalltalk.alert(title, message);
} }
async confirm(message: string, title = '', options: any = {}) { public async confirm(message: string, title = '', options: any = {}) {
try { try {
await smalltalk.confirm(title, message, options); await smalltalk.confirm(title, message, options);
return true; return true;
@ -21,7 +21,7 @@ class Dialogs {
} }
} }
async prompt(message: string, title = '', defaultValue = '', options: any = null) { public async prompt(message: string, title = '', defaultValue = '', options: any = null) {
if (options === null) options = {}; if (options === null) options = {};
try { try {

View File

@ -26,7 +26,7 @@ interface ContextMenuProps {
} }
export default class NoteListUtils { export default class NoteListUtils {
static makeContextMenu(noteIds: string[], props: ContextMenuProps) { public static makeContextMenu(noteIds: string[], props: ContextMenuProps) {
const cmdService = CommandService.instance(); const cmdService = CommandService.instance();
const menuUtils = new MenuUtils(cmdService); const menuUtils = new MenuUtils(cmdService);
@ -212,7 +212,7 @@ export default class NoteListUtils {
return menu; return menu;
} }
static async confirmDeleteNotes(noteIds: string[]) { public static async confirmDeleteNotes(noteIds: string[]) {
if (!noteIds.length) return; if (!noteIds.length) return;
const msg = await Note.deleteMessage(noteIds); const msg = await Note.deleteMessage(noteIds);

View File

@ -65,7 +65,7 @@ class GotoAnything {
public static Dialog: any; public static Dialog: any;
public static manifest: any; public static manifest: any;
onTrigger(event: any) { public onTrigger(event: any) {
this.dispatch({ this.dispatch({
type: 'PLUGINLEGACY_DIALOG_SET', type: 'PLUGINLEGACY_DIALOG_SET',
open: true, open: true,
@ -85,7 +85,7 @@ class Dialog extends React.PureComponent<Props, State> {
private markupToHtml_: MarkupToHtml; private markupToHtml_: MarkupToHtml;
private userCallback_: any = null; private userCallback_: any = null;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
const startString = props?.userData?.startString ? props?.userData?.startString : ''; const startString = props?.userData?.startString ? props?.userData?.startString : '';
@ -119,7 +119,7 @@ class Dialog extends React.PureComponent<Props, State> {
if (startString) this.scheduleListUpdate(); if (startString) this.scheduleListUpdate();
} }
style() { public style() {
const styleKey = [this.props.themeId, this.state.listType, this.state.resultsInBody ? '1' : '0'].join('-'); const styleKey = [this.props.themeId, this.state.listType, this.state.resultsInBody ? '1' : '0'].join('-');
if (this.styles_[styleKey]) return this.styles_[styleKey]; if (this.styles_[styleKey]) return this.styles_[styleKey];
@ -184,7 +184,7 @@ class Dialog extends React.PureComponent<Props, State> {
return this.styles_[styleKey]; return this.styles_[styleKey];
} }
componentDidMount() { public componentDidMount() {
document.addEventListener('keydown', this.onKeyDown); document.addEventListener('keydown', this.onKeyDown);
this.props.dispatch({ this.props.dispatch({
@ -193,7 +193,7 @@ class Dialog extends React.PureComponent<Props, State> {
}); });
} }
componentWillUnmount() { public componentWillUnmount() {
if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_); if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_);
document.removeEventListener('keydown', this.onKeyDown); document.removeEventListener('keydown', this.onKeyDown);
@ -203,7 +203,7 @@ class Dialog extends React.PureComponent<Props, State> {
}); });
} }
onKeyDown(event: any) { public onKeyDown(event: any) {
if (event.keyCode === 27) { // ESCAPE if (event.keyCode === 27) { // ESCAPE
this.props.dispatch({ this.props.dispatch({
pluginName: PLUGIN_NAME, pluginName: PLUGIN_NAME,
@ -213,7 +213,7 @@ class Dialog extends React.PureComponent<Props, State> {
} }
} }
modalLayer_onClick(event: any) { private modalLayer_onClick(event: any) {
if (event.currentTarget === event.target) { if (event.currentTarget === event.target) {
this.props.dispatch({ this.props.dispatch({
pluginName: PLUGIN_NAME, pluginName: PLUGIN_NAME,
@ -223,17 +223,17 @@ class Dialog extends React.PureComponent<Props, State> {
} }
} }
helpButton_onClick() { private helpButton_onClick() {
this.setState({ showHelp: !this.state.showHelp }); this.setState({ showHelp: !this.state.showHelp });
} }
input_onChange(event: any) { private input_onChange(event: any) {
this.setState({ query: event.target.value }); this.setState({ query: event.target.value });
this.scheduleListUpdate(); this.scheduleListUpdate();
} }
scheduleListUpdate() { public scheduleListUpdate() {
if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_); if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_);
this.listUpdateIID_ = shim.setTimeout(async () => { this.listUpdateIID_ = shim.setTimeout(async () => {
@ -242,12 +242,12 @@ class Dialog extends React.PureComponent<Props, State> {
}, 100); }, 100);
} }
async keywords(searchQuery: string) { public async keywords(searchQuery: string) {
const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery); const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery);
return SearchEngine.instance().allParsedQueryTerms(parsedQuery); return SearchEngine.instance().allParsedQueryTerms(parsedQuery);
} }
markupToHtml() { public markupToHtml() {
if (this.markupToHtml_) return this.markupToHtml_; if (this.markupToHtml_) return this.markupToHtml_;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml(); this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return this.markupToHtml_; return this.markupToHtml_;
@ -262,7 +262,7 @@ class Dialog extends React.PureComponent<Props, State> {
}; };
} }
async updateList() { public async updateList() {
let resultsInBody = false; let resultsInBody = false;
if (!this.state.query) { if (!this.state.query) {
@ -402,7 +402,7 @@ class Dialog extends React.PureComponent<Props, State> {
this.itemListRef.current.makeItemIndexVisible(index); this.itemListRef.current.makeItemIndexVisible(index);
} }
async gotoItem(item: any) { public async gotoItem(item: any) {
this.props.dispatch({ this.props.dispatch({
pluginName: PLUGIN_NAME, pluginName: PLUGIN_NAME,
type: 'PLUGINLEGACY_DIALOG_SET', type: 'PLUGINLEGACY_DIALOG_SET',
@ -465,7 +465,7 @@ class Dialog extends React.PureComponent<Props, State> {
} }
} }
listItem_onClick(event: any) { private listItem_onClick(event: any) {
const itemId = event.currentTarget.getAttribute('data-id'); const itemId = event.currentTarget.getAttribute('data-id');
const parentId = event.currentTarget.getAttribute('data-parent-id'); const parentId = event.currentTarget.getAttribute('data-parent-id');
const itemType = Number(event.currentTarget.getAttribute('data-type')); const itemType = Number(event.currentTarget.getAttribute('data-type'));
@ -478,7 +478,7 @@ class Dialog extends React.PureComponent<Props, State> {
}); });
} }
renderItem(item: SearchResult) { public renderItem(item: SearchResult) {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = this.style(); const style = this.style();
const isSelected = item.id === this.state.selectedItemId; const isSelected = item.id === this.state.selectedItemId;
@ -502,7 +502,7 @@ class Dialog extends React.PureComponent<Props, State> {
); );
} }
selectedItemIndex(results: any[] = undefined, itemId: string = undefined) { public selectedItemIndex(results: any[] = undefined, itemId: string = undefined) {
if (typeof results === 'undefined') results = this.state.results; if (typeof results === 'undefined') results = this.state.results;
if (typeof itemId === 'undefined') itemId = this.state.selectedItemId; if (typeof itemId === 'undefined') itemId = this.state.selectedItemId;
for (let i = 0; i < results.length; i++) { for (let i = 0; i < results.length; i++) {
@ -512,13 +512,13 @@ class Dialog extends React.PureComponent<Props, State> {
return -1; return -1;
} }
selectedItem() { public selectedItem() {
const index = this.selectedItemIndex(); const index = this.selectedItemIndex();
if (index < 0) return null; if (index < 0) return null;
return { ...this.state.results[index], commandArgs: this.state.commandArgs }; return { ...this.state.results[index], commandArgs: this.state.commandArgs };
} }
input_onKeyDown(event: any) { private input_onKeyDown(event: any) {
const keyCode = event.keyCode; const keyCode = event.keyCode;
if (this.state.results.length > 0 && (keyCode === 40 || keyCode === 38)) { // DOWN / UP if (this.state.results.length > 0 && (keyCode === 40 || keyCode === 38)) { // DOWN / UP
@ -554,7 +554,7 @@ class Dialog extends React.PureComponent<Props, State> {
return maxItemCount * itemHeight; return maxItemCount * itemHeight;
} }
renderList() { public renderList() {
const style = this.style(); const style = this.style();
const itemListStyle = { const itemListStyle = {
@ -573,7 +573,7 @@ class Dialog extends React.PureComponent<Props, State> {
); );
} }
render() { public render() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
const style = this.style(); const style = this.style();
const helpComp = !this.state.showHelp ? null : <div className="help-text" style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>; const helpComp = !this.state.showHelp ? null : <div className="help-text" style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;

View File

@ -6,24 +6,24 @@ import KvStore from '@joplin/lib/services/KvStore';
export default class PluginAssetsLoader { export default class PluginAssetsLoader {
static instance_: PluginAssetsLoader = null; private static instance_: PluginAssetsLoader = null;
logger_: any = null; private logger_: any = null;
static instance() { public static instance() {
if (PluginAssetsLoader.instance_) return PluginAssetsLoader.instance_; if (PluginAssetsLoader.instance_) return PluginAssetsLoader.instance_;
PluginAssetsLoader.instance_ = new PluginAssetsLoader(); PluginAssetsLoader.instance_ = new PluginAssetsLoader();
return PluginAssetsLoader.instance_; return PluginAssetsLoader.instance_;
} }
setLogger(logger: any) { public setLogger(logger: any) {
this.logger_ = logger; this.logger_ = logger;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
async importAssets() { public async importAssets() {
const destDir = `${Setting.value('resourceDir')}/pluginAssets`; const destDir = `${Setting.value('resourceDir')}/pluginAssets`;
await shim.fsDriver().mkdir(destDir); await shim.fsDriver().mkdir(destDir);

View File

@ -2,7 +2,7 @@ const { BackButtonService } = require('../services/back-button.js');
const DialogBox = require('react-native-dialogbox').default; const DialogBox = require('react-native-dialogbox').default;
export default class BackButtonDialogBox extends DialogBox { export default class BackButtonDialogBox extends DialogBox {
constructor(props: any) { public constructor(props: any) {
super(props); super(props);
this.backHandler_ = () => { this.backHandler_ = () => {
@ -14,7 +14,7 @@ export default class BackButtonDialogBox extends DialogBox {
}; };
} }
async componentDidUpdate() { public async componentDidUpdate() {
if (this.state.isVisible) { if (this.state.isVisible) {
BackButtonService.addHandler(this.backHandler_); BackButtonService.addHandler(this.backHandler_);
} else { } else {

View File

@ -14,7 +14,7 @@ import Setting from '@joplin/lib/models/Setting';
Icon.loadFont().catch((error: any) => { console.info(error); }); Icon.loadFont().catch((error: any) => { console.info(error); });
class CameraView extends Component { class CameraView extends Component {
constructor() { public constructor() {
super(); super();
const dimensions = Dimensions.get('window'); const dimensions = Dimensions.get('window');
@ -34,18 +34,18 @@ class CameraView extends Component {
this.onLayout = this.onLayout.bind(this); this.onLayout = this.onLayout.bind(this);
} }
onLayout(event: any) { public onLayout(event: any) {
this.setState({ this.setState({
screenWidth: event.nativeEvent.layout.width, screenWidth: event.nativeEvent.layout.width,
screenHeight: event.nativeEvent.layout.height, screenHeight: event.nativeEvent.layout.height,
}); });
} }
back_onPress() { private back_onPress() {
if (this.props.onCancel) this.props.onCancel(); if (this.props.onCancel) this.props.onCancel();
} }
reverse_onPress() { private reverse_onPress() {
if (this.props.cameraType === RNCamera.Constants.Type.back) { if (this.props.cameraType === RNCamera.Constants.Type.back) {
Setting.setValue('camera.type', RNCamera.Constants.Type.front); Setting.setValue('camera.type', RNCamera.Constants.Type.front);
} else { } else {
@ -53,7 +53,7 @@ class CameraView extends Component {
} }
} }
ratio_onPress() { private ratio_onPress() {
if (this.state.ratios.length <= 1) return; if (this.state.ratios.length <= 1) return;
let index = this.state.ratios.indexOf(this.props.cameraRatio); let index = this.state.ratios.indexOf(this.props.cameraRatio);
@ -62,7 +62,7 @@ class CameraView extends Component {
Setting.setValue('camera.ratio', this.state.ratios[index]); Setting.setValue('camera.ratio', this.state.ratios[index]);
} }
async photo_onPress() { private async photo_onPress() {
if (!this.camera || !this.props.onPhoto) return; if (!this.camera || !this.props.onPhoto) return;
this.setState({ snapping: true }); this.setState({ snapping: true });
@ -79,14 +79,14 @@ class CameraView extends Component {
} }
async onCameraReady() { public async onCameraReady() {
if (this.supportsRatios()) { if (this.supportsRatios()) {
const ratios = await this.camera.getSupportedRatiosAsync(); const ratios = await this.camera.getSupportedRatiosAsync();
this.setState({ ratios: ratios }); this.setState({ ratios: ratios });
} }
} }
renderButton(onPress: Function, iconNameOrIcon: any, style: any) { public renderButton(onPress: Function, iconNameOrIcon: any, style: any) {
let icon = null; let icon = null;
if (typeof iconNameOrIcon === 'string') { if (typeof iconNameOrIcon === 'string') {
@ -112,7 +112,7 @@ class CameraView extends Component {
); );
} }
fitRectIntoBounds(rect: any, bounds: any) { public fitRectIntoBounds(rect: any, bounds: any) {
const rectRatio = rect.width / rect.height; const rectRatio = rect.width / rect.height;
const boundsRatio = bounds.width / bounds.height; const boundsRatio = bounds.width / bounds.height;
@ -130,7 +130,7 @@ class CameraView extends Component {
return newDimensions; return newDimensions;
} }
cameraRect(ratio: string) { public cameraRect(ratio: string) {
// To keep the calculations simpler, it's assumed that the phone is in // To keep the calculations simpler, it's assumed that the phone is in
// portrait orientation. Then at the end we swap the values if needed. // portrait orientation. Then at the end we swap the values if needed.
const splitted = ratio.split(':'); const splitted = ratio.split(':');
@ -152,11 +152,11 @@ class CameraView extends Component {
return output; return output;
} }
supportsRatios() { public supportsRatios() {
return shim.mobilePlatform() === 'android'; return shim.mobilePlatform() === 'android';
} }
render() { public render() {
const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera'; const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera';
const displayRatios = this.supportsRatios() && this.state.ratios.length > 1; const displayRatios = this.supportsRatios() && this.state.ratios.length > 1;

View File

@ -52,7 +52,7 @@ const styles = StyleSheet.create({
export default class SelectDateTimeDialog extends React.PureComponent<any, any> { export default class SelectDateTimeDialog extends React.PureComponent<any, any> {
constructor(props: any) { public constructor(props: any) {
super(props); super(props);
this.state = { this.state = {
@ -67,37 +67,37 @@ export default class SelectDateTimeDialog extends React.PureComponent<any, any>
this.onSetDate = this.onSetDate.bind(this); this.onSetDate = this.onSetDate.bind(this);
} }
UNSAFE_componentWillReceiveProps(newProps: any) { public UNSAFE_componentWillReceiveProps(newProps: any) {
if (newProps.date !== this.state.date) { if (newProps.date !== this.state.date) {
this.setState({ date: newProps.date }); this.setState({ date: newProps.date });
} }
} }
onAccept() { public onAccept() {
if (this.props.onAccept) this.props.onAccept(this.state.date); if (this.props.onAccept) this.props.onAccept(this.state.date);
} }
onReject() { public onReject() {
if (this.props.onReject) this.props.onReject(); if (this.props.onReject) this.props.onReject();
} }
onClear() { public onClear() {
if (this.props.onAccept) this.props.onAccept(null); if (this.props.onAccept) this.props.onAccept(null);
} }
onPickerConfirm(selectedDate: Date) { public onPickerConfirm(selectedDate: Date) {
this.setState({ date: selectedDate, showPicker: false }); this.setState({ date: selectedDate, showPicker: false });
} }
onPickerCancel() { public onPickerCancel() {
this.setState({ showPicker: false }); this.setState({ showPicker: false });
} }
onSetDate() { public onSetDate() {
this.setState({ showPicker: true }); this.setState({ showPicker: true });
} }
renderContent() { public renderContent() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
return ( return (
@ -118,7 +118,7 @@ export default class SelectDateTimeDialog extends React.PureComponent<any, any>
); );
} }
render() { public render() {
const modalVisible = this.props.shown; const modalVisible = this.props.shown;
if (!modalVisible) return null; if (!modalVisible) return null;

View File

@ -4,7 +4,7 @@ import { Dimensions } from 'react-native';
import { State } from '@joplin/lib/reducer'; import { State } from '@joplin/lib/reducer';
class SideMenuComponent extends SideMenu_ { class SideMenuComponent extends SideMenu_ {
onLayoutChange(e: any) { public onLayoutChange(e: any) {
const { width, height } = e.nativeEvent.layout; const { width, height } = e.nativeEvent.layout;
const openMenuOffsetPercentage = this.props.openMenuOffset / Dimensions.get('window').width; const openMenuOffsetPercentage = this.props.openMenuOffset / Dimensions.get('window').width;
const openMenuOffset = width * openMenuOffsetPercentage; const openMenuOffset = width * openMenuOffsetPercentage;

View File

@ -25,7 +25,7 @@ class AppNavComponent extends Component<Props, State> {
private keyboardDidHideListener: EmitterSubscription|null = null; private keyboardDidHideListener: EmitterSubscription|null = null;
private keyboardWillChangeFrameListener: EmitterSubscription|null = null; private keyboardWillChangeFrameListener: EmitterSubscription|null = null;
constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
this.previousRouteName_ = null; this.previousRouteName_ = null;
@ -35,7 +35,7 @@ class AppNavComponent extends Component<Props, State> {
}; };
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this)); this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this)); this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this));
@ -43,7 +43,7 @@ class AppNavComponent extends Component<Props, State> {
} }
} }
componentWillUnmount() { public componentWillUnmount() {
this.keyboardDidShowListener?.remove(); this.keyboardDidShowListener?.remove();
this.keyboardDidHideListener?.remove(); this.keyboardDidHideListener?.remove();
this.keyboardWillChangeFrameListener?.remove(); this.keyboardWillChangeFrameListener?.remove();
@ -53,15 +53,15 @@ class AppNavComponent extends Component<Props, State> {
this.keyboardWillChangeFrameListener = null; this.keyboardWillChangeFrameListener = null;
} }
keyboardDidShow() { public keyboardDidShow() {
this.setState({ autoCompletionBarExtraHeight: 30 }); this.setState({ autoCompletionBarExtraHeight: 30 });
} }
keyboardDidHide() { public keyboardDidHide() {
this.setState({ autoCompletionBarExtraHeight: 0 }); this.setState({ autoCompletionBarExtraHeight: 0 });
} }
keyboardWillChangeFrame = (evt: KeyboardEvent) => { private keyboardWillChangeFrame = (evt: KeyboardEvent) => {
const windowWidth = Dimensions.get('window').width; const windowWidth = Dimensions.get('window').width;
// If the keyboard isn't as wide as the window, the floating keyboard is diabled. // If the keyboard isn't as wide as the window, the floating keyboard is diabled.
@ -71,7 +71,7 @@ class AppNavComponent extends Component<Props, State> {
}); });
}; };
render() { public render() {
if (!this.props.route) throw new Error('Route must not be null'); if (!this.props.route) throw new Error('Route must not be null');
// Note: certain screens are kept into memory, in particular Notes and Search // Note: certain screens are kept into memory, in particular Notes and Search

View File

@ -25,7 +25,7 @@ import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
import { openDocumentTree } from '@joplin/react-native-saf-x'; import { openDocumentTree } from '@joplin/react-native-saf-x';
class ConfigScreenComponent extends BaseScreenComponent { class ConfigScreenComponent extends BaseScreenComponent {
static navigationOptions(): any { public static navigationOptions(): any {
return { header: null }; return { header: null };
} }
@ -200,7 +200,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
}; };
} }
async checkFilesystemPermission() { public async checkFilesystemPermission() {
if (Platform.OS !== 'android') { if (Platform.OS !== 'android') {
// Not implemented yet // Not implemented yet
return true; return true;
@ -212,11 +212,11 @@ class ConfigScreenComponent extends BaseScreenComponent {
}); });
} }
UNSAFE_componentWillMount() { public UNSAFE_componentWillMount() {
this.setState({ settings: this.props.settings }); this.setState({ settings: this.props.settings });
} }
styles() { public styles() {
const themeId = this.props.themeId; const themeId = this.props.themeId;
const theme = themeStyle(themeId); const theme = themeStyle(themeId);
@ -376,7 +376,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
BackButtonService.removeHandler(this.handleBackButtonPress); BackButtonService.removeHandler(this.handleBackButtonPress);
} }
renderHeader(key: string, title: string) { public renderHeader(key: string, title: string) {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
return ( return (
<View key={key} style={this.styles().headerWrapperStyle} onLayout={(event: any) => this.onHeaderLayout(key, event)}> <View key={key} style={this.styles().headerWrapperStyle} onLayout={(event: any) => this.onHeaderLayout(key, event)}>
@ -410,7 +410,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
); );
} }
sectionToComponent(key: string, section: any, settings: any) { public sectionToComponent(key: string, section: any, settings: any) {
const settingComps = []; const settingComps = [];
for (let i = 0; i < section.metadatas.length; i++) { for (let i = 0; i < section.metadatas.length; i++) {
@ -474,7 +474,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
return !hasDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder; return !hasDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder;
} }
settingToComponent(key: string, value: any) { public settingToComponent(key: string, value: any) {
const themeId = this.props.themeId; const themeId = this.props.themeId;
const theme = themeStyle(themeId); const theme = themeStyle(themeId);
const output: any = null; const output: any = null;
@ -599,7 +599,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
return output; return output;
} }
render() { public render() {
const settings = this.state.settings; const settings = this.state.settings;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);

View File

@ -51,11 +51,11 @@ const emptyArray: any[] = [];
const logger = Logger.create('screens/Note'); const logger = Logger.create('screens/Note');
class NoteScreenComponent extends BaseScreenComponent { class NoteScreenComponent extends BaseScreenComponent {
static navigationOptions(): any { public static navigationOptions(): any {
return { header: null }; return { header: null };
} }
constructor() { public constructor() {
super(); super();
this.state = { this.state = {
note: Note.new(), note: Note.new(),
@ -280,7 +280,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}); });
} }
screenHeader_undoButtonPress() { private screenHeader_undoButtonPress() {
if (this.useEditorBeta()) { if (this.useEditorBeta()) {
this.editorRef.current.undo(); this.editorRef.current.undo();
} else { } else {
@ -288,7 +288,7 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
screenHeader_redoButtonPress() { private screenHeader_redoButtonPress() {
if (this.useEditorBeta()) { if (this.useEditorBeta()) {
this.editorRef.current.redo(); this.editorRef.current.redo();
} else { } else {
@ -296,13 +296,13 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
undoState(noteBody: string = null) { public undoState(noteBody: string = null) {
return { return {
body: noteBody === null ? this.state.note.body : noteBody, body: noteBody === null ? this.state.note.body : noteBody,
}; };
} }
styles() { public styles() {
const themeId = this.props.themeId; const themeId = this.props.themeId;
const theme = themeStyle(themeId); const theme = themeStyle(themeId);
@ -392,11 +392,11 @@ class NoteScreenComponent extends BaseScreenComponent {
return this.styles_[cacheKey]; return this.styles_[cacheKey];
} }
isModified() { public isModified() {
return shared.isModified(this); return shared.isModified(this);
} }
async requestGeoLocationPermissions() { public async requestGeoLocationPermissions() {
if (!Setting.value('trackLocation')) return; if (!Setting.value('trackLocation')) return;
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, {
@ -413,7 +413,7 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
async componentDidMount() { public async componentDidMount() {
BackButtonService.addHandler(this.backHandler); BackButtonService.addHandler(this.backHandler);
NavService.addHandler(this.navHandler); NavService.addHandler(this.navHandler);
@ -436,11 +436,11 @@ class NoteScreenComponent extends BaseScreenComponent {
void this.requestGeoLocationPermissions(); void this.requestGeoLocationPermissions();
} }
onMarkForDownload(event: any) { public onMarkForDownload(event: any) {
void ResourceFetcher.instance().markForDownload(event.resourceId); void ResourceFetcher.instance().markForDownload(event.resourceId);
} }
componentDidUpdate(prevProps: any) { public componentDidUpdate(prevProps: any) {
if (this.doFocusUpdate_) { if (this.doFocusUpdate_) {
this.doFocusUpdate_ = false; this.doFocusUpdate_ = false;
this.focusUpdate(); this.focusUpdate();
@ -454,7 +454,7 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
componentWillUnmount() { public componentWillUnmount() {
BackButtonService.removeHandler(this.backHandler); BackButtonService.removeHandler(this.backHandler);
NavService.removeHandler(this.navHandler); NavService.removeHandler(this.navHandler);
@ -467,13 +467,13 @@ class NoteScreenComponent extends BaseScreenComponent {
if (this.undoRedoService_) this.undoRedoService_.off('stackChange', this.undoRedoService_stackChange); if (this.undoRedoService_) this.undoRedoService_.off('stackChange', this.undoRedoService_stackChange);
} }
title_changeText(text: string) { private title_changeText(text: string) {
shared.noteComponent_change(this, 'title', text); shared.noteComponent_change(this, 'title', text);
this.setState({ newAndNoTitleChangeNoteId: null }); this.setState({ newAndNoTitleChangeNoteId: null });
this.scheduleSave(); this.scheduleSave();
} }
body_changeText(text: string) { private body_changeText(text: string) {
if (!this.undoRedoService_.canUndo) { if (!this.undoRedoService_.canUndo) {
this.undoRedoService_.push(this.undoState()); this.undoRedoService_.push(this.undoState());
} else { } else {
@ -484,7 +484,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.scheduleSave(); this.scheduleSave();
} }
body_selectionChange(event: any) { private body_selectionChange(event: any) {
if (this.useEditorBeta()) { if (this.useEditorBeta()) {
this.selection = event.selection; this.selection = event.selection;
} else { } else {
@ -492,34 +492,34 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
makeSaveAction() { public makeSaveAction() {
return async () => { return async () => {
return shared.saveNoteButton_press(this, null, null); return shared.saveNoteButton_press(this, null, null);
}; };
} }
saveActionQueue(noteId: string) { public saveActionQueue(noteId: string) {
if (!this.saveActionQueues_[noteId]) { if (!this.saveActionQueues_[noteId]) {
this.saveActionQueues_[noteId] = new AsyncActionQueue(500); this.saveActionQueues_[noteId] = new AsyncActionQueue(500);
} }
return this.saveActionQueues_[noteId]; return this.saveActionQueues_[noteId];
} }
scheduleSave() { public scheduleSave() {
this.saveActionQueue(this.state.note.id).push(this.makeSaveAction()); this.saveActionQueue(this.state.note.id).push(this.makeSaveAction());
} }
async saveNoteButton_press(folderId: string = null) { private async saveNoteButton_press(folderId: string = null) {
await shared.saveNoteButton_press(this, folderId, null); await shared.saveNoteButton_press(this, folderId, null);
Keyboard.dismiss(); Keyboard.dismiss();
} }
async saveOneProperty(name: string, value: any) { public async saveOneProperty(name: string, value: any) {
await shared.saveOneProperty(this, name, value); await shared.saveOneProperty(this, name, value);
} }
async deleteNote_onPress() { private async deleteNote_onPress() {
const note = this.state.note; const note = this.state.note;
if (!note.id) return; if (!note.id) return;
@ -546,7 +546,7 @@ class NoteScreenComponent extends BaseScreenComponent {
return result; return result;
} }
async imageDimensions(uri: string) { public async imageDimensions(uri: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Image.getSize( Image.getSize(
uri, uri,
@ -560,7 +560,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}); });
} }
async resizeImage(localFilePath: string, targetPath: string, mimeType: string) { public async resizeImage(localFilePath: string, targetPath: string, mimeType: string) {
const maxSize = Resource.IMAGE_MAX_DIMENSION; const maxSize = Resource.IMAGE_MAX_DIMENSION;
const dimensions: any = await this.imageDimensions(localFilePath); const dimensions: any = await this.imageDimensions(localFilePath);
@ -609,7 +609,7 @@ class NoteScreenComponent extends BaseScreenComponent {
return true; return true;
} }
async attachFile(pickerResponse: any, fileType: string) { public async attachFile(pickerResponse: any, fileType: string) {
if (!pickerResponse) { if (!pickerResponse) {
// User has cancelled // User has cancelled
return; return;
@ -727,11 +727,11 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
takePhoto_onPress() { private takePhoto_onPress() {
this.setState({ showCamera: true }); this.setState({ showCamera: true });
} }
cameraView_onPhoto(data: any) { private cameraView_onPhoto(data: any) {
void this.attachFile( void this.attachFile(
{ {
uri: data.uri, uri: data.uri,
@ -743,7 +743,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ showCamera: false }); this.setState({ showCamera: false });
} }
cameraView_onCancel() { private cameraView_onCancel() {
this.setState({ showCamera: false }); this.setState({ showCamera: false });
} }
@ -754,34 +754,30 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
toggleIsTodo_onPress() { private toggleIsTodo_onPress() {
shared.toggleIsTodo_onPress(this); shared.toggleIsTodo_onPress(this);
this.scheduleSave(); this.scheduleSave();
} }
tags_onPress() { private tags_onPress() {
if (!this.state.note || !this.state.note.id) return; if (!this.state.note || !this.state.note.id) return;
this.setState({ noteTagDialogShown: true }); this.setState({ noteTagDialogShown: true });
} }
async share_onPress() { private async share_onPress() {
await Share.share({ await Share.share({
message: `${this.state.note.title}\n\n${this.state.note.body}`, message: `${this.state.note.title}\n\n${this.state.note.body}`,
title: this.state.note.title, title: this.state.note.title,
}); });
} }
properties_onPress() { private properties_onPress() {
this.props.dispatch({ type: 'SIDE_MENU_OPEN' }); this.props.dispatch({ type: 'SIDE_MENU_OPEN' });
} }
setAlarm_onPress() { public async onAlarmDialogAccept(date: Date) {
this.setState({ alarmDialogShown: true });
}
async onAlarmDialogAccept(date: Date) {
const newNote = Object.assign({}, this.state.note); const newNote = Object.assign({}, this.state.note);
newNote.todo_due = date ? date.getTime() : 0; newNote.todo_due = date ? date.getTime() : 0;
@ -790,11 +786,11 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ alarmDialogShown: false }); this.setState({ alarmDialogShown: false });
} }
onAlarmDialogReject() { public onAlarmDialogReject() {
this.setState({ alarmDialogShown: false }); this.setState({ alarmDialogShown: false });
} }
async showOnMap_onPress() { private async showOnMap_onPress() {
if (!this.state.note.id) return; if (!this.state.note.id) return;
const note = await Note.load(this.state.note.id); const note = await Note.load(this.state.note.id);
@ -807,7 +803,7 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
async showSource_onPress() { private async showSource_onPress() {
if (!this.state.note.id) return; if (!this.state.note.id) return;
const note = await Note.load(this.state.note.id); const note = await Note.load(this.state.note.id);
@ -818,12 +814,12 @@ class NoteScreenComponent extends BaseScreenComponent {
} }
} }
copyMarkdownLink_onPress() { private copyMarkdownLink_onPress() {
const note = this.state.note; const note = this.state.note;
Clipboard.setString(Note.markdownTag(note)); Clipboard.setString(Note.markdownTag(note));
} }
sideMenuOptions() { public sideMenuOptions() {
const note = this.state.note; const note = this.state.note;
if (!note) return []; if (!note) return [];
@ -854,7 +850,7 @@ class NoteScreenComponent extends BaseScreenComponent {
return output; return output;
} }
async showAttachMenu() { public async showAttachMenu() {
const buttons = []; const buttons = [];
// On iOS, it will show "local files", which means certain files saved from the browser // On iOS, it will show "local files", which means certain files saved from the browser
@ -875,7 +871,7 @@ class NoteScreenComponent extends BaseScreenComponent {
if (buttonId === 'attachPhoto') void this.attachPhoto_onPress(); if (buttonId === 'attachPhoto') void this.attachPhoto_onPress();
} }
menuOptions() { public menuOptions() {
const note = this.state.note; const note = this.state.note;
const isTodo = note && !!note.is_todo; const isTodo = note && !!note.is_todo;
const isSaved = note && note.id; const isSaved = note && note.id;
@ -959,11 +955,11 @@ class NoteScreenComponent extends BaseScreenComponent {
return output; return output;
} }
async todoCheckbox_change(checked: boolean) { private async todoCheckbox_change(checked: boolean) {
await this.saveOneProperty('todo_completed', checked ? time.unixMs() : 0); await this.saveOneProperty('todo_completed', checked ? time.unixMs() : 0);
} }
scheduleFocusUpdate() { public scheduleFocusUpdate() {
if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_); if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_);
this.focusUpdateIID_ = shim.setTimeout(() => { this.focusUpdateIID_ = shim.setTimeout(() => {
@ -972,7 +968,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}, 100); }, 100);
} }
focusUpdate() { public focusUpdate() {
if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_); if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_);
this.focusUpdateIID_ = null; this.focusUpdateIID_ = null;
@ -990,7 +986,7 @@ class NoteScreenComponent extends BaseScreenComponent {
// } // }
} }
async folderPickerOptions_valueChanged(itemValue: any) { private async folderPickerOptions_valueChanged(itemValue: any) {
const note = this.state.note; const note = this.state.note;
const isProvisionalNote = this.props.provisionalNoteIds.includes(note.id); const isProvisionalNote = this.props.provisionalNoteIds.includes(note.id);
@ -1011,7 +1007,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}); });
} }
folderPickerOptions() { public folderPickerOptions() {
const options = { const options = {
enabled: true, enabled: true,
selectedFolderId: this.state.folder ? this.state.folder.id : null, selectedFolderId: this.state.folder ? this.state.folder.id : null,
@ -1024,7 +1020,7 @@ class NoteScreenComponent extends BaseScreenComponent {
return this.folderPickerOptions_; return this.folderPickerOptions_;
} }
onBodyViewerLoadEnd() { public onBodyViewerLoadEnd() {
shim.setTimeout(() => { shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 1 }); this.setState({ HACK_webviewLoadingState: 1 });
shim.setTimeout(() => { shim.setTimeout(() => {
@ -1033,11 +1029,11 @@ class NoteScreenComponent extends BaseScreenComponent {
}, 5); }, 5);
} }
onBodyViewerCheckboxChange(newBody: string) { public onBodyViewerCheckboxChange(newBody: string) {
void this.saveOneProperty('body', newBody); void this.saveOneProperty('body', newBody);
} }
render() { public render() {
if (this.state.isLoading) { if (this.state.isLoading) {
return ( return (
<View style={this.styles().screen}> <View style={this.styles().screen}>

View File

@ -21,7 +21,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
private onAppStateChangeSub_: NativeEventSubscription = null; private onAppStateChangeSub_: NativeEventSubscription = null;
constructor() { public constructor() {
super(); super();
this.onAppStateChange_ = async () => { this.onAppStateChange_ = async () => {
@ -78,7 +78,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
}; };
} }
styles() { public styles() {
if (!this.styles_) this.styles_ = {}; if (!this.styles_) this.styles_ = {};
const themeId = this.props.themeId; const themeId = this.props.themeId;
const cacheKey = themeId; const cacheKey = themeId;
@ -96,24 +96,24 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
return this.styles_[cacheKey]; return this.styles_[cacheKey];
} }
async componentDidMount() { public async componentDidMount() {
BackButtonService.addHandler(this.backHandler); BackButtonService.addHandler(this.backHandler);
await this.refreshNotes(); await this.refreshNotes();
this.onAppStateChangeSub_ = RNAppState.addEventListener('change', this.onAppStateChange_); this.onAppStateChangeSub_ = RNAppState.addEventListener('change', this.onAppStateChange_);
} }
async componentWillUnmount() { public async componentWillUnmount() {
if (this.onAppStateChangeSub_) this.onAppStateChangeSub_.remove(); if (this.onAppStateChangeSub_) this.onAppStateChangeSub_.remove();
BackButtonService.removeHandler(this.backHandler); BackButtonService.removeHandler(this.backHandler);
} }
async componentDidUpdate(prevProps: any) { public async componentDidUpdate(prevProps: any) {
if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType) { if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType) {
await this.refreshNotes(this.props); await this.refreshNotes(this.props);
} }
} }
async refreshNotes(props: any = null) { public async refreshNotes(props: any = null) {
if (props === null) props = this.props; if (props === null) props = this.props;
const options = { const options = {
@ -149,36 +149,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
}); });
} }
deleteFolder_onPress(folderId: string) { public newNoteNavigate = async (folderId: string, isTodo: boolean) => {
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
dialogs.confirm(this, _('Delete notebook? All notes and sub-notebooks within this notebook will also be deleted.')).then((ok: boolean) => {
if (!ok) return;
Folder.delete(folderId)
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
.then(() => {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Notes',
smartFilterId: 'c3176726992c11e9ac940492261af972',
});
})
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
.catch(error => {
alert(error.message);
});
});
}
editFolder_onPress(folderId: string) {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Folder',
folderId: folderId,
});
}
newNoteNavigate = async (folderId: string, isTodo: boolean) => {
const newNote = await Note.save({ const newNote = await Note.save({
parent_id: folderId, parent_id: folderId,
is_todo: isTodo ? 1 : 0, is_todo: isTodo ? 1 : 0,
@ -191,7 +162,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
}); });
}; };
parentItem(props: any = null) { public parentItem(props: any = null) {
if (!props) props = this.props; if (!props) props = this.props;
let output = null; let output = null;
@ -208,7 +179,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
return output; return output;
} }
folderPickerOptions() { public folderPickerOptions() {
const options = { const options = {
enabled: this.props.noteSelectionEnabled, enabled: this.props.noteSelectionEnabled,
mustSelect: true, mustSelect: true,
@ -220,7 +191,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
return this.folderPickerOptions_; return this.folderPickerOptions_;
} }
render() { public render() {
const parent = this.parentItem(); const parent = this.parentItem();
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);

View File

@ -27,11 +27,11 @@ class SearchScreenComponent extends BaseScreenComponent {
private styles_: any = {}; private styles_: any = {};
private scheduleSearchTimer_: any = null; private scheduleSearchTimer_: any = null;
static navigationOptions() { public static navigationOptions() {
return { header: null } as any; return { header: null } as any;
} }
constructor() { public constructor() {
super(); super();
this.state = { this.state = {
query: '', query: '',
@ -39,7 +39,7 @@ class SearchScreenComponent extends BaseScreenComponent {
}; };
} }
styles() { public styles() {
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId]; if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
@ -72,17 +72,17 @@ class SearchScreenComponent extends BaseScreenComponent {
return this.styles_[this.props.themeId]; return this.styles_[this.props.themeId];
} }
componentDidMount() { public componentDidMount() {
this.setState({ query: this.props.query }); this.setState({ query: this.props.query });
void this.refreshSearch(this.props.query); void this.refreshSearch(this.props.query);
this.isMounted_ = true; this.isMounted_ = true;
} }
componentWillUnmount() { public componentWillUnmount() {
this.isMounted_ = false; this.isMounted_ = false;
} }
clearButton_press() { private clearButton_press() {
this.props.dispatch({ this.props.dispatch({
type: 'SEARCH_QUERY', type: 'SEARCH_QUERY',
query: '', query: '',
@ -92,7 +92,7 @@ class SearchScreenComponent extends BaseScreenComponent {
void this.refreshSearch(''); void this.refreshSearch('');
} }
async refreshSearch(query: string = null) { public async refreshSearch(query: string = null) {
if (!this.props.visible) return; if (!this.props.visible) return;
query = gotoAnythingStyleQuery(query); query = gotoAnythingStyleQuery(query);
@ -130,7 +130,7 @@ class SearchScreenComponent extends BaseScreenComponent {
this.setState({ notes: notes }); this.setState({ notes: notes });
} }
scheduleSearch() { public scheduleSearch() {
if (this.scheduleSearchTimer_) clearTimeout(this.scheduleSearchTimer_); if (this.scheduleSearchTimer_) clearTimeout(this.scheduleSearchTimer_);
this.scheduleSearchTimer_ = setTimeout(() => { this.scheduleSearchTimer_ = setTimeout(() => {
@ -139,7 +139,7 @@ class SearchScreenComponent extends BaseScreenComponent {
}, 200); }, 200);
} }
searchTextInput_changeText(text: string) { private searchTextInput_changeText(text: string) {
this.setState({ query: text }); this.setState({ query: text });
this.props.dispatch({ this.props.dispatch({
@ -150,7 +150,7 @@ class SearchScreenComponent extends BaseScreenComponent {
this.scheduleSearch(); this.scheduleSearch();
} }
render() { public render() {
if (!this.isMounted_) return null; if (!this.isMounted_) return null;
const theme = themeStyle(this.props.themeId); const theme = themeStyle(this.props.themeId);

View File

@ -8,7 +8,7 @@ export default class AlarmServiceDriver {
private inAppNotificationHandler_: any = null; private inAppNotificationHandler_: any = null;
private logger_: Logger; private logger_: Logger;
constructor(logger: Logger) { public constructor(logger: Logger) {
this.logger_ = logger; this.logger_ = logger;
PushNotificationIOS.addEventListener('localNotification', (instance: any) => { PushNotificationIOS.addEventListener('localNotification', (instance: any) => {
if (!this.inAppNotificationHandler_) return; if (!this.inAppNotificationHandler_) return;
@ -23,19 +23,19 @@ export default class AlarmServiceDriver {
}); });
} }
hasPersistentNotifications() { public hasPersistentNotifications() {
return true; return true;
} }
notificationIsSet() { public notificationIsSet() {
throw new Error('Available only for non-persistent alarms'); throw new Error('Available only for non-persistent alarms');
} }
setInAppNotificationHandler(v: any) { public setInAppNotificationHandler(v: any) {
this.inAppNotificationHandler_ = v; this.inAppNotificationHandler_ = v;
} }
async hasPermissions(perm: any = null) { public async hasPermissions(perm: any = null) {
if (perm !== null) return perm.alert && perm.badge && perm.sound; if (perm !== null) return perm.alert && perm.badge && perm.sound;
if (this.hasPermission_ !== null) return this.hasPermission_; if (this.hasPermission_ !== null) return this.hasPermission_;
@ -49,7 +49,7 @@ export default class AlarmServiceDriver {
}); });
} }
async requestPermissions() { public async requestPermissions() {
const options: any = { const options: any = {
alert: 1, alert: 1,
badge: 1, badge: 1,
@ -60,11 +60,11 @@ export default class AlarmServiceDriver {
return this.hasPermissions(newPerm); return this.hasPermissions(newPerm);
} }
async clearNotification(id: number) { public async clearNotification(id: number) {
PushNotificationIOS.cancelLocalNotifications({ id: `${id}` }); PushNotificationIOS.cancelLocalNotifications({ id: `${id}` });
} }
async scheduleNotification(notification: Notification) { public async scheduleNotification(notification: Notification) {
if (!(await this.hasPermissions())) { if (!(await this.hasPermissions())) {
const ok = await this.requestPermissions(); const ok = await this.requestPermissions();
if (!ok) return; if (!ok) return;

View File

@ -418,7 +418,7 @@ export default class BaseApplication {
ResourceFetcher.instance().scheduleAutoAddResources(); ResourceFetcher.instance().scheduleAutoAddResources();
} }
reducerActionToString(action: any) { public reducerActionToString(action: any) {
const o = [action.type]; const o = [action.type];
if ('id' in action) o.push(action.id); if ('id' in action) o.push(action.id);
if ('noteId' in action) o.push(action.noteId); if ('noteId' in action) o.push(action.noteId);

View File

@ -85,19 +85,19 @@ class BaseModel {
private static db_: JoplinDatabase; private static db_: JoplinDatabase;
static modelType(): ModelType { public static modelType(): ModelType {
throw new Error('Must be overriden'); throw new Error('Must be overriden');
} }
static tableName(): string { public static tableName(): string {
throw new Error('Must be overriden'); throw new Error('Must be overriden');
} }
static setDb(db: any) { public static setDb(db: any) {
this.db_ = db; this.db_ = db;
} }
static addModelMd(model: any): any { public static addModelMd(model: any): any {
if (!model) return model; if (!model) return model;
if (Array.isArray(model)) { if (Array.isArray(model)) {
@ -113,22 +113,22 @@ class BaseModel {
} }
} }
static logger() { public static logger() {
return this.db().logger(); return this.db().logger();
} }
static useUuid() { public static useUuid() {
return false; return false;
} }
static byId(items: any[], id: string) { public static byId(items: any[], id: string) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (items[i].id === id) return items[i]; if (items[i].id === id) return items[i];
} }
return null; return null;
} }
static defaultValues(fieldNames: string[]) { public static defaultValues(fieldNames: string[]) {
const output: any = {}; const output: any = {};
for (const n of fieldNames) { for (const n of fieldNames) {
output[n] = this.db().fieldDefaultValue(this.tableName(), n); output[n] = this.db().fieldDefaultValue(this.tableName(), n);
@ -136,14 +136,14 @@ class BaseModel {
return output; return output;
} }
static modelIndexById(items: any[], id: string) { public static modelIndexById(items: any[], id: string) {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (items[i].id === id) return i; if (items[i].id === id) return i;
} }
return -1; return -1;
} }
static modelsByIds(items: any[], ids: string[]) { public static modelsByIds(items: any[], ids: string[]) {
const output = []; const output = [];
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (ids.indexOf(items[i].id) >= 0) { if (ids.indexOf(items[i].id) >= 0) {
@ -155,14 +155,14 @@ class BaseModel {
// Prefer the use of this function to compare IDs as it handles the case where // Prefer the use of this function to compare IDs as it handles the case where
// one ID is null and the other is "", in which case they are actually considered to be the same. // one ID is null and the other is "", in which case they are actually considered to be the same.
static idsEqual(id1: string, id2: string) { public static idsEqual(id1: string, id2: string) {
if (!id1 && !id2) return true; if (!id1 && !id2) return true;
if (!id1 && !!id2) return false; if (!id1 && !!id2) return false;
if (!!id1 && !id2) return false; if (!!id1 && !id2) return false;
return id1 === id2; return id1 === id2;
} }
static modelTypeToName(type: number) { public static modelTypeToName(type: number) {
for (let i = 0; i < BaseModel.typeEnum_.length; i++) { for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
const e = BaseModel.typeEnum_[i]; const e = BaseModel.typeEnum_[i];
if (e[1] === type) return e[0].substr(5).toLowerCase(); if (e[1] === type) return e[0].substr(5).toLowerCase();
@ -170,7 +170,7 @@ class BaseModel {
throw new Error(`Unknown model type: ${type}`); throw new Error(`Unknown model type: ${type}`);
} }
static modelNameToType(name: string) { public static modelNameToType(name: string) {
for (let i = 0; i < BaseModel.typeEnum_.length; i++) { for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
const e = BaseModel.typeEnum_[i]; const e = BaseModel.typeEnum_[i];
const eName = e[0].substr(5).toLowerCase(); const eName = e[0].substr(5).toLowerCase();
@ -179,12 +179,12 @@ class BaseModel {
throw new Error(`Unknown model name: ${name}`); throw new Error(`Unknown model name: ${name}`);
} }
static hasField(name: string) { public static hasField(name: string) {
const fields = this.fieldNames(); const fields = this.fieldNames();
return fields.indexOf(name) >= 0; return fields.indexOf(name) >= 0;
} }
static fieldNames(withPrefix: boolean = false) { public static fieldNames(withPrefix: boolean = false) {
const output = this.db().tableFieldNames(this.tableName()); const output = this.db().tableFieldNames(this.tableName());
if (!withPrefix) return output; if (!withPrefix) return output;
@ -197,7 +197,7 @@ class BaseModel {
return temp; return temp;
} }
static fieldType(name: string, defaultValue: any = null) { public static fieldType(name: string, defaultValue: any = null) {
const fields = this.fields(); const fields = this.fields();
for (let i = 0; i < fields.length; i++) { for (let i = 0; i < fields.length; i++) {
if (fields[i].name === name) return fields[i].type; if (fields[i].name === name) return fields[i].type;
@ -206,11 +206,11 @@ class BaseModel {
throw new Error(`Unknown field: ${name}`); throw new Error(`Unknown field: ${name}`);
} }
static fields(): TableField[] { public static fields(): TableField[] {
return this.db().tableFields(this.tableName()); return this.db().tableFields(this.tableName());
} }
static removeUnknownFields(model: any) { public static removeUnknownFields(model: any) {
const newModel: any = {}; const newModel: any = {};
for (const n in model) { for (const n in model) {
if (!model.hasOwnProperty(n)) continue; if (!model.hasOwnProperty(n)) continue;
@ -220,7 +220,7 @@ class BaseModel {
return newModel; return newModel;
} }
static new() { public static new() {
const fields = this.fields(); const fields = this.fields();
const output: any = {}; const output: any = {};
for (let i = 0; i < fields.length; i++) { for (let i = 0; i < fields.length; i++) {
@ -230,7 +230,7 @@ class BaseModel {
return output; return output;
} }
static modOptions(options: any) { public static modOptions(options: any) {
if (!options) { if (!options) {
options = {}; options = {};
} else { } else {
@ -242,7 +242,7 @@ class BaseModel {
return options; return options;
} }
static count(options: any = null) { public static count(options: any = null) {
if (!options) options = {}; if (!options) options = {};
let sql = `SELECT count(*) as total FROM \`${this.tableName()}\``; let sql = `SELECT count(*) as total FROM \`${this.tableName()}\``;
if (options.where) sql += ` WHERE ${options.where}`; if (options.where) sql += ` WHERE ${options.where}`;
@ -254,19 +254,19 @@ class BaseModel {
}); });
} }
static load(id: string, options: any = null) { public static load(id: string, options: any = null) {
return this.loadByField('id', id, options); return this.loadByField('id', id, options);
} }
static shortId(id: string) { public static shortId(id: string) {
return id.substr(0, 5); return id.substr(0, 5);
} }
static loadByPartialId(partialId: string) { public static loadByPartialId(partialId: string) {
return this.modelSelectAll(`SELECT * FROM \`${this.tableName()}\` WHERE \`id\` LIKE ?`, [`${partialId}%`]); return this.modelSelectAll(`SELECT * FROM \`${this.tableName()}\` WHERE \`id\` LIKE ?`, [`${partialId}%`]);
} }
static applySqlOptions(options: any, sql: string, params: any[] = null) { public static applySqlOptions(options: any, sql: string, params: any[] = null) {
if (!options) options = {}; if (!options) options = {};
if (options.order && options.order.length) { if (options.order && options.order.length) {
@ -278,13 +278,13 @@ class BaseModel {
return { sql: sql, params: params }; return { sql: sql, params: params };
} }
static async allIds(options: any = null) { public static async allIds(options: any = null) {
const q = this.applySqlOptions(options, `SELECT id FROM \`${this.tableName()}\``); const q = this.applySqlOptions(options, `SELECT id FROM \`${this.tableName()}\``);
const rows = await this.db().selectAll(q.sql, q.params); const rows = await this.db().selectAll(q.sql, q.params);
return rows.map((r: any) => r.id); return rows.map((r: any) => r.id);
} }
static async all(options: any = null) { public static async all(options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!options.fields) options.fields = '*'; if (!options.fields) options.fields = '*';
@ -299,7 +299,7 @@ class BaseModel {
return this.modelSelectAll(q.sql, q.params); return this.modelSelectAll(q.sql, q.params);
} }
static async byIds(ids: string[], options: any = null) { public static async byIds(ids: string[], options: any = null) {
if (!ids.length) return []; if (!ids.length) return [];
if (!options) options = {}; if (!options) options = {};
if (!options.fields) options.fields = '*'; if (!options.fields) options.fields = '*';
@ -310,7 +310,7 @@ class BaseModel {
return this.modelSelectAll(q.sql); return this.modelSelectAll(q.sql);
} }
static async search(options: any = null) { public static async search(options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!options.fields) options.fields = '*'; if (!options.fields) options.fields = '*';
@ -332,7 +332,7 @@ class BaseModel {
return this.modelSelectAll(query.sql, query.params); return this.modelSelectAll(query.sql, query.params);
} }
static modelSelectOne(sql: string, params: any[] = null) { public static modelSelectOne(sql: string, params: any[] = null) {
if (params === null) params = []; if (params === null) params = [];
return this.db() return this.db()
.selectOne(sql, params) .selectOne(sql, params)
@ -342,7 +342,7 @@ class BaseModel {
}); });
} }
static modelSelectAll(sql: string, params: any[] = null) { public static modelSelectAll(sql: string, params: any[] = null) {
if (params === null) params = []; if (params === null) params = [];
return this.db() return this.db()
.selectAll(sql, params) .selectAll(sql, params)
@ -352,7 +352,7 @@ class BaseModel {
}); });
} }
static loadByField(fieldName: string, fieldValue: any, options: any = null) { public static loadByField(fieldName: string, fieldValue: any, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false; if (!('caseInsensitive' in options)) options.caseInsensitive = false;
if (!options.fields) options.fields = '*'; if (!options.fields) options.fields = '*';
@ -361,7 +361,7 @@ class BaseModel {
return this.modelSelectOne(sql, [fieldValue]); return this.modelSelectOne(sql, [fieldValue]);
} }
static loadByFields(fields: any, options: any = null) { public static loadByFields(fields: any, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false; if (!('caseInsensitive' in options)) options.caseInsensitive = false;
if (!options.fields) options.fields = '*'; if (!options.fields) options.fields = '*';
@ -376,11 +376,11 @@ class BaseModel {
return this.modelSelectOne(sql, params); return this.modelSelectOne(sql, params);
} }
static loadByTitle(fieldValue: any) { public static loadByTitle(fieldValue: any) {
return this.modelSelectOne(`SELECT * FROM \`${this.tableName()}\` WHERE \`title\` = ?`, [fieldValue]); return this.modelSelectOne(`SELECT * FROM \`${this.tableName()}\` WHERE \`title\` = ?`, [fieldValue]);
} }
static diffObjects(oldModel: any, newModel: any) { public static diffObjects(oldModel: any, newModel: any) {
const output: any = {}; const output: any = {};
const fields = this.diffObjectsFields(oldModel, newModel); const fields = this.diffObjectsFields(oldModel, newModel);
for (let i = 0; i < fields.length; i++) { for (let i = 0; i < fields.length; i++) {
@ -390,7 +390,7 @@ class BaseModel {
return output; return output;
} }
static diffObjectsFields(oldModel: any, newModel: any) { public static diffObjectsFields(oldModel: any, newModel: any) {
const output = []; const output = [];
for (const n in newModel) { for (const n in newModel) {
if (!newModel.hasOwnProperty(n)) continue; if (!newModel.hasOwnProperty(n)) continue;
@ -402,13 +402,13 @@ class BaseModel {
return output; return output;
} }
static modelsAreSame(oldModel: any, newModel: any) { public static modelsAreSame(oldModel: any, newModel: any) {
const diff = this.diffObjects(oldModel, newModel); const diff = this.diffObjects(oldModel, newModel);
delete diff.type_; delete diff.type_;
return !Object.getOwnPropertyNames(diff).length; return !Object.getOwnPropertyNames(diff).length;
} }
static saveMutex(modelOrId: any) { public static saveMutex(modelOrId: any) {
const noLockMutex = { const noLockMutex = {
acquire: function(): any { acquire: function(): any {
return null; return null;
@ -429,7 +429,7 @@ class BaseModel {
return mutex; return mutex;
} }
static releaseSaveMutex(modelOrId: any, release: Function) { public static releaseSaveMutex(modelOrId: any, release: Function) {
if (!release) return; if (!release) return;
if (!modelOrId) return release(); if (!modelOrId) return release();
@ -444,7 +444,7 @@ class BaseModel {
release(); release();
} }
static saveQuery(o: any, options: any) { public static saveQuery(o: any, options: any) {
let temp: any = {}; let temp: any = {};
const fieldNames = this.fieldNames(); const fieldNames = this.fieldNames();
for (let i = 0; i < fieldNames.length; i++) { for (let i = 0; i < fieldNames.length; i++) {
@ -521,7 +521,7 @@ class BaseModel {
return query; return query;
} }
static userSideValidation(o: any) { public static userSideValidation(o: any) {
if (o.id && !o.id.match(/^[a-f0-9]{32}$/)) { if (o.id && !o.id.match(/^[a-f0-9]{32}$/)) {
throw new Error('Validation error: ID must a 32-characters lowercase hexadecimal string'); throw new Error('Validation error: ID must a 32-characters lowercase hexadecimal string');
} }
@ -532,7 +532,7 @@ class BaseModel {
} }
} }
static async save(o: any, options: any = null) { public static async save(o: any, options: any = null) {
// When saving, there's a mutex per model ID. This is because the model returned from this function // When saving, there's a mutex per model ID. This is because the model returned from this function
// is basically its input `o` (instead of being read from the database, for performance reasons). // is basically its input `o` (instead of being read from the database, for performance reasons).
// This works well in general except if that model is saved simultaneously in two places. In that // This works well in general except if that model is saved simultaneously in two places. In that
@ -602,7 +602,7 @@ class BaseModel {
return output; return output;
} }
static isNew(object: any, options: any) { public static isNew(object: any, options: any) {
if (options && 'isNew' in options) { if (options && 'isNew' in options) {
// options.isNew can be "auto" too // options.isNew can be "auto" too
if (options.isNew === true) return true; if (options.isNew === true) return true;
@ -612,7 +612,7 @@ class BaseModel {
return !object.id; return !object.id;
} }
static filterArray(models: any[]) { public static filterArray(models: any[]) {
const output = []; const output = [];
for (let i = 0; i < models.length; i++) { for (let i = 0; i < models.length; i++) {
output.push(this.filter(models[i])); output.push(this.filter(models[i]));
@ -620,7 +620,7 @@ class BaseModel {
return output; return output;
} }
static filter(model: any) { public static filter(model: any) {
if (!model) return model; if (!model) return model;
const output = Object.assign({}, model); const output = Object.assign({}, model);
@ -643,12 +643,12 @@ class BaseModel {
return output; return output;
} }
static delete(id: string) { public static delete(id: string) {
if (!id) throw new Error('Cannot delete object without an ID'); if (!id) throw new Error('Cannot delete object without an ID');
return this.db().exec(`DELETE FROM ${this.tableName()} WHERE id = ?`, [id]); return this.db().exec(`DELETE FROM ${this.tableName()} WHERE id = ?`, [id]);
} }
static async batchDelete(ids: string[], options: DeleteOptions = null) { public static async batchDelete(ids: string[], options: DeleteOptions = null) {
if (!ids.length) return; if (!ids.length) return;
options = this.modOptions(options); options = this.modOptions(options);
const idFieldName = options.idFieldName ? options.idFieldName : 'id'; const idFieldName = options.idFieldName ? options.idFieldName : 'id';
@ -656,7 +656,7 @@ class BaseModel {
await this.db().exec(sql); await this.db().exec(sql);
} }
static db() { public static db() {
if (!this.db_) throw new Error('Accessing database before it has been initialised'); if (!this.db_) throw new Error('Accessing database before it has been initialised');
return this.db_; return this.db_;
} }

View File

@ -24,11 +24,11 @@ export default class ClipperServer {
private static instance_: ClipperServer = null; private static instance_: ClipperServer = null;
constructor() { public constructor() {
this.logger_ = new Logger(); this.logger_ = new Logger();
} }
static instance() { public static instance() {
if (this.instance_) return this.instance_; if (this.instance_) return this.instance_;
this.instance_ = new ClipperServer(); this.instance_ = new ClipperServer();
return this.instance_; return this.instance_;
@ -38,30 +38,30 @@ export default class ClipperServer {
return this.api_; return this.api_;
} }
initialize(actionApi: any = null) { public initialize(actionApi: any = null) {
this.api_ = new Api(() => { this.api_ = new Api(() => {
return Setting.value('api.token'); return Setting.value('api.token');
}, (action: any) => { this.dispatch(action); }, actionApi); }, (action: any) => { this.dispatch(action); }, actionApi);
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
setDispatch(d: Function) { public setDispatch(d: Function) {
this.dispatch_ = d; this.dispatch_ = d;
} }
dispatch(action: any) { public dispatch(action: any) {
if (!this.dispatch_) throw new Error('dispatch not set!'); if (!this.dispatch_) throw new Error('dispatch not set!');
this.dispatch_(action); this.dispatch_(action);
} }
setStartState(v: StartState) { public setStartState(v: StartState) {
if (this.startState_ === v) return; if (this.startState_ === v) return;
this.startState_ = v; this.startState_ = v;
this.dispatch({ this.dispatch({
@ -70,7 +70,7 @@ export default class ClipperServer {
}); });
} }
setPort(v: number) { public setPort(v: number) {
if (this.port_ === v) return; if (this.port_ === v) return;
this.port_ = v; this.port_ = v;
this.dispatch({ this.dispatch({
@ -79,7 +79,7 @@ export default class ClipperServer {
}); });
} }
async findAvailablePort() { public async findAvailablePort() {
const tcpPortUsed = require('tcp-port-used'); const tcpPortUsed = require('tcp-port-used');
let state = null; let state = null;
@ -92,14 +92,14 @@ export default class ClipperServer {
throw new Error('All potential ports are in use or not available.'); throw new Error('All potential ports are in use or not available.');
} }
async isRunning() { public async isRunning() {
const tcpPortUsed = require('tcp-port-used'); const tcpPortUsed = require('tcp-port-used');
const port = Setting.value('api.port') ? Setting.value('api.port') : startPort(Setting.value('env')); const port = Setting.value('api.port') ? Setting.value('api.port') : startPort(Setting.value('env'));
const inUse = await tcpPortUsed.check(port); const inUse = await tcpPortUsed.check(port);
return inUse ? port : 0; return inUse ? port : 0;
} }
async start() { public async start() {
this.setPort(null); this.setPort(null);
this.setStartState(StartState.Starting); this.setStartState(StartState.Starting);
@ -237,7 +237,7 @@ export default class ClipperServer {
return new Promise(() => {}); return new Promise(() => {});
} }
async stop() { public async stop() {
this.server_.destroy(); this.server_.destroy();
this.server_ = null; this.server_ = null;
this.setStartState(StartState.Idle); this.setStartState(StartState.Idle);

View File

@ -29,7 +29,7 @@ export default class Cache {
private expirableKeys_: ExpirableKeys = {}; private expirableKeys_: ExpirableKeys = {};
private recordKeyHistory_: string[] = []; private recordKeyHistory_: string[] = [];
constructor(maxRecords: number = 50) { public constructor(maxRecords: number = 50) {
this.maxRecords_ = maxRecords; this.maxRecords_ = maxRecords;
} }

View File

@ -63,7 +63,7 @@ class Logger {
private lastDbCleanup_: number = time.unixMs(); private lastDbCleanup_: number = time.unixMs();
private enabled_: boolean = true; private enabled_: boolean = true;
static fsDriver() { public static fsDriver() {
if (!Logger.fsDriver_) Logger.fsDriver_ = new FsDriverDummy(); if (!Logger.fsDriver_) Logger.fsDriver_ = new FsDriverDummy();
return Logger.fsDriver_; return Logger.fsDriver_;
} }
@ -103,7 +103,7 @@ class Logger {
return this.globalLogger_; return this.globalLogger_;
} }
static create(prefix: string): LoggerWrapper { public static create(prefix: string): LoggerWrapper {
return { return {
debug: (...object: any[]) => this.globalLogger.log(LogLevel.Debug, prefix, ...object), debug: (...object: any[]) => this.globalLogger.log(LogLevel.Debug, prefix, ...object),
info: (...object: any[]) => this.globalLogger.log(LogLevel.Info, prefix, ...object), info: (...object: any[]) => this.globalLogger.log(LogLevel.Info, prefix, ...object),
@ -118,15 +118,15 @@ class Logger {
return previous; return previous;
} }
level() { public level() {
return this.level_; return this.level_;
} }
targets() { public targets() {
return this.targets_; return this.targets_;
} }
addTarget(type: TargetType, options: TargetOptions = null) { public addTarget(type: TargetType, options: TargetOptions = null) {
const target = { type: type }; const target = { type: type };
for (const n in options) { for (const n in options) {
if (!options.hasOwnProperty(n)) continue; if (!options.hasOwnProperty(n)) continue;
@ -136,7 +136,7 @@ class Logger {
this.targets_.push(target); this.targets_.push(target);
} }
objectToString(object: any) { public objectToString(object: any) {
let output = ''; let output = '';
if (typeof object === 'object') { if (typeof object === 'object') {
@ -157,7 +157,7 @@ class Logger {
return output; return output;
} }
objectsToString(...object: any[]) { public objectsToString(...object: any[]) {
const output = []; const output = [];
for (let i = 0; i < object.length; i++) { for (let i = 0; i < object.length; i++) {
output.push(`"${this.objectToString(object[i])}"`); output.push(`"${this.objectToString(object[i])}"`);
@ -165,7 +165,7 @@ class Logger {
return output.join(', '); return output.join(', ');
} }
static databaseCreateTableSql() { public static databaseCreateTableSql() {
const output = ` const output = `
CREATE TABLE IF NOT EXISTS logs ( CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
@ -179,7 +179,7 @@ class Logger {
} }
// Only for database at the moment // Only for database at the moment
async lastEntries(limit: number = 100, options: any = null) { public async lastEntries(limit: number = 100, options: any = null) {
if (options === null) options = {}; if (options === null) options = {};
if (!options.levels) options.levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error]; if (!options.levels) options.levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
if (!options.levels.length) return []; if (!options.levels.length) return [];
@ -195,7 +195,7 @@ class Logger {
return []; return [];
} }
targetLevel(target: Target) { public targetLevel(target: Target) {
if ('level' in target) return target.level; if ('level' in target) return target.level;
return this.level(); return this.level();
} }
@ -287,20 +287,20 @@ class Logger {
} }
} }
error(...object: any[]) { public error(...object: any[]) {
return this.log(LogLevel.Error, null, ...object); return this.log(LogLevel.Error, null, ...object);
} }
warn(...object: any[]) { public warn(...object: any[]) {
return this.log(LogLevel.Warn, null, ...object); return this.log(LogLevel.Warn, null, ...object);
} }
info(...object: any[]) { public info(...object: any[]) {
return this.log(LogLevel.Info, null, ...object); return this.log(LogLevel.Info, null, ...object);
} }
debug(...object: any[]) { public debug(...object: any[]) {
return this.log(LogLevel.Debug, null, ...object); return this.log(LogLevel.Debug, null, ...object);
} }
static levelStringToId(s: string) { public static levelStringToId(s: string) {
if (s === 'none') return LogLevel.None; if (s === 'none') return LogLevel.None;
if (s === 'error') return LogLevel.Error; if (s === 'error') return LogLevel.Error;
if (s === 'warn') return LogLevel.Warn; if (s === 'warn') return LogLevel.Warn;
@ -309,7 +309,7 @@ class Logger {
throw new Error(`Unknown log level: ${s}`); throw new Error(`Unknown log level: ${s}`);
} }
static levelIdToString(id: LogLevel) { public static levelIdToString(id: LogLevel) {
if (id === LogLevel.None) return 'none'; if (id === LogLevel.None) return 'none';
if (id === LogLevel.Error) return 'error'; if (id === LogLevel.Error) return 'error';
if (id === LogLevel.Warn) return 'warn'; if (id === LogLevel.Warn) return 'warn';
@ -318,7 +318,7 @@ class Logger {
throw new Error(`Unknown level ID: ${id}`); throw new Error(`Unknown level ID: ${id}`);
} }
static levelIds() { public static levelIds() {
return [LogLevel.None, LogLevel.Error, LogLevel.Warn, LogLevel.Info, LogLevel.Debug]; return [LogLevel.None, LogLevel.Error, LogLevel.Warn, LogLevel.Info, LogLevel.Debug];
} }

View File

@ -12,20 +12,20 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
private api_: any; private api_: any;
static id() { public static id() {
return 3; return 3;
} }
constructor(db: any, options: any = null) { public constructor(db: any, options: any = null) {
super(db, options); super(db, options);
this.api_ = null; this.api_ = null;
} }
static targetName() { public static targetName() {
return 'onedrive'; return 'onedrive';
} }
static label() { public static label() {
return _('OneDrive'); return _('OneDrive');
} }
@ -37,30 +37,30 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
return false; return false;
} }
async isAuthenticated() { public async isAuthenticated() {
return !!this.api().auth(); return !!this.api().auth();
} }
syncTargetId() { public syncTargetId() {
return SyncTargetOneDrive.id(); return SyncTargetOneDrive.id();
} }
isTesting() { public isTesting() {
const p = parameters(); const p = parameters();
return !!p.oneDriveTest; return !!p.oneDriveTest;
} }
oneDriveParameters() { public oneDriveParameters() {
const p = parameters(); const p = parameters();
if (p.oneDriveTest) return p.oneDriveTest; if (p.oneDriveTest) return p.oneDriveTest;
return p.oneDrive; return p.oneDrive;
} }
authRouteName() { public authRouteName() {
return 'OneDriveLogin'; return 'OneDriveLogin';
} }
api() { public api() {
if (this.isTesting()) { if (this.isTesting()) {
return this.fileApi_.driver().api(); return this.fileApi_.driver().api();
} }
@ -92,7 +92,7 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
return this.api_; return this.api_;
} }
async initFileApi() { public async initFileApi() {
let context = Setting.value(`sync.${this.syncTargetId()}.context`); let context = Setting.value(`sync.${this.syncTargetId()}.context`);
context = context === '' ? null : JSON.parse(context); context = context === '' ? null : JSON.parse(context);
let accountProperties = context ? context.accountProperties : null; let accountProperties = context ? context.accountProperties : null;
@ -109,7 +109,7 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
return fileApi; return fileApi;
} }
async initSynchronizer() { public async initSynchronizer() {
try { try {
if (!(await this.isAuthenticated())) throw new Error('User is not authentified'); if (!(await this.isAuthenticated())) throw new Error('User is not authentified');
return new Synchronizer(this.db(), await this.fileApi(), Setting.value('appType')); return new Synchronizer(this.db(), await this.fileApi(), Setting.value('appType'));

View File

@ -91,31 +91,31 @@ export default class Synchronizer {
this.apiCall = this.apiCall.bind(this); this.apiCall = this.apiCall.bind(this);
} }
state() { public state() {
return this.state_; return this.state_;
} }
db() { public db() {
return this.db_; return this.db_;
} }
api() { public api() {
return this.api_; return this.api_;
} }
clientId() { public clientId() {
return this.clientId_; return this.clientId_;
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
lockHandler() { public lockHandler() {
if (this.lockHandler_) return this.lockHandler_; if (this.lockHandler_) return this.lockHandler_;
this.lockHandler_ = new LockHandler(this.api()); this.lockHandler_ = new LockHandler(this.api());
return this.lockHandler_; return this.lockHandler_;
@ -127,13 +127,13 @@ export default class Synchronizer {
return this.lockClientType_; return this.lockClientType_;
} }
migrationHandler() { public migrationHandler() {
if (this.migrationHandler_) return this.migrationHandler_; if (this.migrationHandler_) return this.migrationHandler_;
this.migrationHandler_ = new MigrationHandler(this.api(), this.db(), this.lockHandler(), this.lockClientType(), this.clientId_); this.migrationHandler_ = new MigrationHandler(this.api(), this.db(), this.lockHandler(), this.lockClientType(), this.clientId_);
return this.migrationHandler_; return this.migrationHandler_;
} }
maxResourceSize() { public maxResourceSize() {
if (this.maxResourceSize_ !== null) return this.maxResourceSize_; if (this.maxResourceSize_ !== null) return this.maxResourceSize_;
return this.appType_ === AppType.Mobile ? 100 * 1000 * 1000 : Infinity; return this.appType_ === AppType.Mobile ? 100 * 1000 * 1000 : Infinity;
} }
@ -146,7 +146,7 @@ export default class Synchronizer {
this.encryptionService_ = v; this.encryptionService_ = v;
} }
encryptionService() { public encryptionService() {
return this.encryptionService_; return this.encryptionService_;
} }
@ -158,7 +158,7 @@ export default class Synchronizer {
return this.resourceService_; return this.resourceService_;
} }
async waitForSyncToFinish() { public async waitForSyncToFinish() {
if (this.state() === 'idle') return; if (this.state() === 'idle') return;
while (true) { while (true) {
@ -177,7 +177,7 @@ export default class Synchronizer {
return `${duration}ms`; return `${duration}ms`;
} }
static reportToLines(report: any) { public static reportToLines(report: any) {
const lines = []; const lines = [];
if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal)); if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal));
if (report.updateLocal) lines.push(_('Updated local items: %d.', report.updateLocal)); if (report.updateLocal) lines.push(_('Updated local items: %d.', report.updateLocal));
@ -193,7 +193,7 @@ export default class Synchronizer {
return lines; return lines;
} }
logSyncOperation(action: string, local: any = null, remote: RemoteItem = null, message: string = null, actionCount: number = 1) { public logSyncOperation(action: string, local: any = null, remote: RemoteItem = null, message: string = null, actionCount: number = 1) {
const line = ['Sync']; const line = ['Sync'];
line.push(action); line.push(action);
if (message) line.push(message); if (message) line.push(message);
@ -237,7 +237,7 @@ export default class Synchronizer {
this.dispatch({ type: 'SYNC_REPORT_UPDATE', report: reportCopy }); this.dispatch({ type: 'SYNC_REPORT_UPDATE', report: reportCopy });
} }
async logSyncSummary(report: any) { public async logSyncSummary(report: any) {
logger.info('Operations completed: '); logger.info('Operations completed: ');
for (const n in report) { for (const n in report) {
if (!report.hasOwnProperty(n)) continue; if (!report.hasOwnProperty(n)) continue;
@ -265,7 +265,7 @@ export default class Synchronizer {
} }
} }
async cancel() { public async cancel() {
if (this.cancelling_ || this.state() === 'idle') return; if (this.cancelling_ || this.state() === 'idle') return;
// Stop queue but don't set it to null as it may be used to // Stop queue but don't set it to null as it may be used to
@ -285,11 +285,11 @@ export default class Synchronizer {
}); });
} }
cancelling() { public cancelling() {
return this.cancelling_; return this.cancelling_;
} }
logLastRequests() { public logLastRequests() {
const lastRequests = this.api().lastRequests(); const lastRequests = this.api().lastRequests();
if (!lastRequests || !lastRequests.length) return; if (!lastRequests || !lastRequests.length) return;
@ -300,17 +300,17 @@ export default class Synchronizer {
} }
} }
static stateToLabel(state: string) { public static stateToLabel(state: string) {
if (state === 'idle') return _('Idle'); if (state === 'idle') return _('Idle');
if (state === 'in_progress') return _('In progress'); if (state === 'in_progress') return _('In progress');
return state; return state;
} }
isFullSync(steps: string[]) { public isFullSync(steps: string[]) {
return steps.includes('update_remote') && steps.includes('delete_remote') && steps.includes('delta'); return steps.includes('update_remote') && steps.includes('delete_remote') && steps.includes('delta');
} }
async lockErrorStatus_() { private async lockErrorStatus_() {
const locks = await this.lockHandler().locks(); const locks = await this.lockHandler().locks();
const currentDate = await this.lockHandler().currentDate(); const currentDate = await this.lockHandler().currentDate();

View File

@ -23,16 +23,16 @@ export default class TaskQueue {
private name_: string; private name_: string;
private logger_: Logger; private logger_: Logger;
constructor(name: string, logger: Logger = null) { public constructor(name: string, logger: Logger = null) {
this.name_ = name; this.name_ = name;
this.logger_ = logger ? logger : new Logger(); this.logger_ = logger ? logger : new Logger();
} }
concurrency() { public concurrency() {
return Setting.value('sync.maxConcurrentConnections'); return Setting.value('sync.maxConcurrentConnections');
} }
push(id: string, callback: Function) { public push(id: string, callback: Function) {
if (this.stopping_) throw new Error('Cannot push task when queue is stopping'); if (this.stopping_) throw new Error('Cannot push task when queue is stopping');
this.waitingTasks_.push({ this.waitingTasks_.push({
@ -42,7 +42,7 @@ export default class TaskQueue {
this.processQueue_(); this.processQueue_();
} }
processQueue_() { private processQueue_() {
if (this.processingQueue_ || this.stopping_) return; if (this.processingQueue_ || this.stopping_) return;
this.processingQueue_ = true; this.processingQueue_ = true;
@ -84,19 +84,19 @@ export default class TaskQueue {
this.processingQueue_ = false; this.processingQueue_ = false;
} }
isWaiting(taskId: string) { public isWaiting(taskId: string) {
return this.waitingTasks_.find(task => task.id === taskId); return this.waitingTasks_.find(task => task.id === taskId);
} }
isProcessing(taskId: string) { public isProcessing(taskId: string) {
return taskId in this.processingTasks_; return taskId in this.processingTasks_;
} }
isDone(taskId: string) { public isDone(taskId: string) {
return taskId in this.results_; return taskId in this.results_;
} }
async waitForAll() { public async waitForAll() {
return new Promise((resolve) => { return new Promise((resolve) => {
const checkIID = setInterval(() => { const checkIID = setInterval(() => {
if (this.waitingTasks_.length) return; if (this.waitingTasks_.length) return;
@ -107,16 +107,16 @@ export default class TaskQueue {
}); });
} }
taskExists(taskId: string) { public taskExists(taskId: string) {
return this.isWaiting(taskId) || this.isProcessing(taskId) || this.isDone(taskId); return this.isWaiting(taskId) || this.isProcessing(taskId) || this.isDone(taskId);
} }
taskResult(taskId: string) { public taskResult(taskId: string) {
if (!this.taskExists(taskId)) throw new Error(`No such task: ${taskId}`); if (!this.taskExists(taskId)) throw new Error(`No such task: ${taskId}`);
return this.results_[taskId]; return this.results_[taskId];
} }
async waitForResult(taskId: string) { public async waitForResult(taskId: string) {
if (!this.taskExists(taskId)) throw new Error(`No such task: ${taskId}`); if (!this.taskExists(taskId)) throw new Error(`No such task: ${taskId}`);
while (true) { while (true) {
@ -126,7 +126,7 @@ export default class TaskQueue {
} }
} }
async stop() { public async stop() {
this.stopping_ = true; this.stopping_ = true;
this.logger_.info(`TaskQueue.stop: ${this.name_}: waiting for tasks to complete: ${Object.keys(this.processingTasks_).length}`); this.logger_.info(`TaskQueue.stop: ${this.name_}: waiting for tasks to complete: ${Object.keys(this.processingTasks_).length}`);
@ -146,7 +146,7 @@ export default class TaskQueue {
this.logger_.info(`TaskQueue.stop: ${this.name_}: Done, waited for ${Date.now() - startTime}`); this.logger_.info(`TaskQueue.stop: ${this.name_}: Done, waited for ${Date.now() - startTime}`);
} }
isStopping() { public isStopping() {
return this.stopping_; return this.stopping_;
} }
} }

View File

@ -46,7 +46,7 @@ export default class DatabaseDriverBetterSqlite {
return this.db_.prepare(sql).run(params ? params : []); return this.db_.prepare(sql).run(params ? params : []);
} }
lastInsertId() { public lastInsertId() {
throw new Error('NOT IMPLEMENTED'); throw new Error('NOT IMPLEMENTED');
} }
} }

View File

@ -35,30 +35,30 @@ export default class Database {
this.driver_ = driver; this.driver_ = driver;
} }
setLogExcludedQueryTypes(v: string[]) { public setLogExcludedQueryTypes(v: string[]) {
this.logExcludedQueryTypes_ = v; this.logExcludedQueryTypes_ = v;
} }
// Converts the SQLite error to a regular JS error // Converts the SQLite error to a regular JS error
// so that it prints a stacktrace when passed to // so that it prints a stacktrace when passed to
// console.error() // console.error()
sqliteErrorToJsError(error: any, sql: string = null, params: SqlParams = null) { public sqliteErrorToJsError(error: any, sql: string = null, params: SqlParams = null) {
return this.driver().sqliteErrorToJsError(error, sql, params); return this.driver().sqliteErrorToJsError(error, sql, params);
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
driver() { public driver() {
return this.driver_; return this.driver_;
} }
async open(options: any) { public async open(options: any) {
try { try {
await this.driver().open(options); await this.driver().open(options);
} catch (error) { } catch (error) {
@ -97,7 +97,7 @@ export default class Database {
return output.join(','); return output.join(',');
} }
async tryCall(callName: string, inputSql: StringOrSqlQuery, inputParams: SqlParams) { public async tryCall(callName: string, inputSql: StringOrSqlQuery, inputParams: SqlParams) {
let sql: string = null; let sql: string = null;
let params: SqlParams = null; let params: SqlParams = null;
@ -157,11 +157,11 @@ export default class Database {
} }
} }
async selectOne(sql: string, params: SqlParams = null): Promise<Row> { public async selectOne(sql: string, params: SqlParams = null): Promise<Row> {
return this.tryCall('selectOne', sql, params); return this.tryCall('selectOne', sql, params);
} }
async loadExtension(/* path */) { public async loadExtension(/* path */) {
return; // Disabled for now as fuzzy search extension is not in use return; // Disabled for now as fuzzy search extension is not in use
// let result = null; // let result = null;
@ -173,11 +173,11 @@ export default class Database {
// } // }
} }
async selectAll(sql: string, params: SqlParams = null): Promise<Row[]> { public async selectAll(sql: string, params: SqlParams = null): Promise<Row[]> {
return this.tryCall('selectAll', sql, params); return this.tryCall('selectAll', sql, params);
} }
async selectAllFields(sql: string, params: SqlParams, field: string): Promise<any[]> { public async selectAllFields(sql: string, params: SqlParams, field: string): Promise<any[]> {
const rows = await this.tryCall('selectAll', sql, params); const rows = await this.tryCall('selectAll', sql, params);
const output = []; const output = [];
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@ -188,11 +188,11 @@ export default class Database {
return output; return output;
} }
async exec(sql: StringOrSqlQuery, params: SqlParams = null) { public async exec(sql: StringOrSqlQuery, params: SqlParams = null) {
return this.tryCall('exec', sql, params); return this.tryCall('exec', sql, params);
} }
async transactionExecBatch(queries: StringOrSqlQuery[]) { public async transactionExecBatch(queries: StringOrSqlQuery[]) {
if (queries.length <= 0) return; if (queries.length <= 0) return;
if (queries.length === 1) { if (queries.length === 1) {
@ -221,7 +221,7 @@ export default class Database {
} }
} }
static enumId(type: string, s: string) { public static enumId(type: string, s: string) {
if (type === 'settings') { if (type === 'settings') {
if (s === 'int') return 1; if (s === 'int') return 1;
if (s === 'string') return 2; if (s === 'string') return 2;
@ -240,7 +240,7 @@ export default class Database {
throw new Error(`Unknown enum type or value: ${type}, ${s}`); throw new Error(`Unknown enum type or value: ${type}, ${s}`);
} }
static enumName(type: string, id: number) { public static enumName(type: string, id: number) {
if (type === 'fieldType') { if (type === 'fieldType') {
if (id === Database.TYPE_UNKNOWN) return 'unknown'; if (id === Database.TYPE_UNKNOWN) return 'unknown';
if (id === Database.TYPE_INT) return 'int'; if (id === Database.TYPE_INT) return 'int';
@ -253,7 +253,7 @@ export default class Database {
return undefined; return undefined;
} }
static formatValue(type: number, value: any) { public static formatValue(type: number, value: any) {
if (value === null || value === undefined) return null; if (value === null || value === undefined) return null;
if (type === this.TYPE_INT) return Number(value); if (type === this.TYPE_INT) return Number(value);
if (type === this.TYPE_TEXT) return value; if (type === this.TYPE_TEXT) return value;
@ -261,7 +261,7 @@ export default class Database {
throw new Error(`Unknown type: ${type}`); throw new Error(`Unknown type: ${type}`);
} }
sqlStringToLines(sql: string) { public sqlStringToLines(sql: string) {
const output = []; const output = [];
const lines = sql.split('\n'); const lines = sql.split('\n');
let statement = ''; let statement = '';
@ -279,7 +279,7 @@ export default class Database {
return output; return output;
} }
logQuery(sql: string, params: SqlParams = null) { public logQuery(sql: string, params: SqlParams = null) {
if (!this.sqlQueryLogEnabled_) return; if (!this.sqlQueryLogEnabled_) return;
if (this.logExcludedQueryTypes_.length) { if (this.logExcludedQueryTypes_.length) {
@ -293,7 +293,7 @@ export default class Database {
if (params !== null && params.length) this.logger().debug(JSON.stringify(params)); if (params !== null && params.length) this.logger().debug(JSON.stringify(params));
} }
static insertQuery(tableName: string, data: Record<string, any>) { public static insertQuery(tableName: string, data: Record<string, any>) {
if (!data || !Object.keys(data).length) throw new Error('Data is empty'); if (!data || !Object.keys(data).length) throw new Error('Data is empty');
let keySql = ''; let keySql = '';
@ -314,7 +314,7 @@ export default class Database {
}; };
} }
static updateQuery(tableName: string, data: Record<string, any>, where: string | Record<string, any>) { public static updateQuery(tableName: string, data: Record<string, any>, where: string | Record<string, any>) {
if (!data || !Object.keys(data).length) throw new Error('Data is empty'); if (!data || !Object.keys(data).length) throw new Error('Data is empty');
let sql = ''; let sql = '';
@ -343,7 +343,7 @@ export default class Database {
}; };
} }
alterColumnQueries(tableName: string, fields: Record<string, string>) { public alterColumnQueries(tableName: string, fields: Record<string, string>) {
const fieldsNoType = []; const fieldsNoType = [];
for (const n in fields) { for (const n in fields) {
if (!fields.hasOwnProperty(n)) continue; if (!fields.hasOwnProperty(n)) continue;
@ -373,7 +373,7 @@ export default class Database {
return sql.trim().split('\n'); return sql.trim().split('\n');
} }
wrapQueries(queries: any[]) { public wrapQueries(queries: any[]) {
const output = []; const output = [];
for (let i = 0; i < queries.length; i++) { for (let i = 0; i < queries.length; i++) {
output.push(this.wrapQuery(queries[i])); output.push(this.wrapQuery(queries[i]));
@ -381,7 +381,7 @@ export default class Database {
return output; return output;
} }
wrapQuery(sql: any, params: SqlParams = null): SqlQuery { public wrapQuery(sql: any, params: SqlParams = null): SqlQuery {
if (!sql) throw new Error(`Cannot wrap empty string: ${sql}`); if (!sql) throw new Error(`Cannot wrap empty string: ${sql}`);
if (Array.isArray(sql)) { if (Array.isArray(sql)) {

View File

@ -9,11 +9,11 @@ export class EventManager {
private appStateWatchedProps_: string[]; private appStateWatchedProps_: string[];
private appStateListeners_: any; private appStateListeners_: any;
constructor() { public constructor() {
this.reset(); this.reset();
} }
reset() { public reset() {
this.emitter_ = new events.EventEmitter(); this.emitter_ = new events.EventEmitter();
this.appStatePrevious_ = {}; this.appStatePrevious_ = {};
@ -21,27 +21,27 @@ export class EventManager {
this.appStateListeners_ = {}; this.appStateListeners_ = {};
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.emitter_.on(eventName, callback); return this.emitter_.on(eventName, callback);
} }
emit(eventName: string, object: any = null) { public emit(eventName: string, object: any = null) {
return this.emitter_.emit(eventName, object); return this.emitter_.emit(eventName, object);
} }
removeListener(eventName: string, callback: Function) { public removeListener(eventName: string, callback: Function) {
return this.emitter_.removeListener(eventName, callback); return this.emitter_.removeListener(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.removeListener(eventName, callback); return this.removeListener(eventName, callback);
} }
filterOn(filterName: string, callback: Function) { public filterOn(filterName: string, callback: Function) {
return this.emitter_.on(`filter:${filterName}`, callback); return this.emitter_.on(`filter:${filterName}`, callback);
} }
filterOff(filterName: string, callback: Function) { public filterOff(filterName: string, callback: Function) {
return this.removeListener(`filter:${filterName}`, callback); return this.removeListener(`filter:${filterName}`, callback);
} }
@ -67,7 +67,7 @@ export class EventManager {
return output; return output;
} }
appStateOn(propName: string, callback: Function) { public appStateOn(propName: string, callback: Function) {
if (!this.appStateListeners_[propName]) { if (!this.appStateListeners_[propName]) {
this.appStateListeners_[propName] = []; this.appStateListeners_[propName] = [];
this.appStateWatchedProps_.push(propName); this.appStateWatchedProps_.push(propName);
@ -76,7 +76,7 @@ export class EventManager {
this.appStateListeners_[propName].push(callback); this.appStateListeners_[propName].push(callback);
} }
appStateOff(propName: string, callback: Function) { public appStateOff(propName: string, callback: Function) {
if (!this.appStateListeners_[propName]) { if (!this.appStateListeners_[propName]) {
throw new Error('EventManager: Trying to unregister a state prop watch for a non-watched prop (1)'); throw new Error('EventManager: Trying to unregister a state prop watch for a non-watched prop (1)');
} }
@ -87,7 +87,7 @@ export class EventManager {
this.appStateListeners_[propName].splice(idx, 1); this.appStateListeners_[propName].splice(idx, 1);
} }
stateValue_(state: any, propName: string) { private stateValue_(state: any, propName: string) {
const parts = propName.split('.'); const parts = propName.split('.');
let s = state; let s = state;
for (const p of parts) { for (const p of parts) {
@ -100,7 +100,7 @@ export class EventManager {
// This function works by keeping a copy of the watched props and, whenever this function // This function works by keeping a copy of the watched props and, whenever this function
// is called, comparing the previous and new values and emitting events if they have changed. // is called, comparing the previous and new values and emitting events if they have changed.
// The appStateEmit function should be called from a middleware. // The appStateEmit function should be called from a middleware.
appStateEmit(state: any) { public appStateEmit(state: any) {
if (!this.appStateWatchedProps_.length) return; if (!this.appStateWatchedProps_.length) return;
for (const propName of this.appStateWatchedProps_) { for (const propName of this.appStateWatchedProps_) {

View File

@ -7,12 +7,12 @@ export default class FileApiDriverMemory {
private items_: any[]; private items_: any[];
private deletedItems_: any[]; private deletedItems_: any[];
constructor() { public constructor() {
this.items_ = []; this.items_ = [];
this.deletedItems_ = []; this.deletedItems_ = [];
} }
encodeContent_(content: any) { private encodeContent_(content: any) {
if (content instanceof Buffer) { if (content instanceof Buffer) {
return content.toString('base64'); return content.toString('base64');
} else { } else {
@ -28,23 +28,23 @@ export default class FileApiDriverMemory {
return true; return true;
} }
decodeContent_(content: any) { private decodeContent_(content: any) {
return Buffer.from(content, 'base64').toString('utf-8'); return Buffer.from(content, 'base64').toString('utf-8');
} }
itemIndexByPath(path: string) { public itemIndexByPath(path: string) {
for (let i = 0; i < this.items_.length; i++) { for (let i = 0; i < this.items_.length; i++) {
if (this.items_[i].path === path) return i; if (this.items_[i].path === path) return i;
} }
return -1; return -1;
} }
itemByPath(path: string) { public itemByPath(path: string) {
const index = this.itemIndexByPath(path); const index = this.itemIndexByPath(path);
return index < 0 ? null : this.items_[index]; return index < 0 ? null : this.items_[index];
} }
newItem(path: string, isDir = false) { public newItem(path: string, isDir = false) {
const now = time.unixMs(); const now = time.unixMs();
return { return {
path: path, path: path,
@ -55,18 +55,18 @@ export default class FileApiDriverMemory {
}; };
} }
stat(path: string) { public stat(path: string) {
const item = this.itemByPath(path); const item = this.itemByPath(path);
return Promise.resolve(item ? Object.assign({}, item) : null); return Promise.resolve(item ? Object.assign({}, item) : null);
} }
async setTimestamp(path: string, timestampMs: number): Promise<any> { public async setTimestamp(path: string, timestampMs: number): Promise<any> {
const item = this.itemByPath(path); const item = this.itemByPath(path);
if (!item) return Promise.reject(new Error(`File not found: ${path}`)); if (!item) return Promise.reject(new Error(`File not found: ${path}`));
item.updated_time = timestampMs; item.updated_time = timestampMs;
} }
async list(path: string) { public async list(path: string) {
const output = []; const output = [];
for (let i = 0; i < this.items_.length; i++) { for (let i = 0; i < this.items_.length; i++) {
@ -89,7 +89,7 @@ export default class FileApiDriverMemory {
}); });
} }
async get(path: string, options: any) { public async get(path: string, options: any) {
const item = this.itemByPath(path); const item = this.itemByPath(path);
if (!item) return Promise.resolve(null); if (!item) return Promise.resolve(null);
if (item.isDir) return Promise.reject(new Error(`${path} is a directory, not a file`)); if (item.isDir) return Promise.reject(new Error(`${path} is a directory, not a file`));
@ -105,13 +105,13 @@ export default class FileApiDriverMemory {
return output; return output;
} }
async mkdir(path: string) { public async mkdir(path: string) {
const index = this.itemIndexByPath(path); const index = this.itemIndexByPath(path);
if (index >= 0) return; if (index >= 0) return;
this.items_.push(this.newItem(path, true)); this.items_.push(this.newItem(path, true));
} }
async put(path: string, content: any, options: any = null) { public async put(path: string, content: any, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (options.source === 'file') content = await fs.readFile(options.path); if (options.source === 'file') content = await fs.readFile(options.path);
@ -152,7 +152,7 @@ export default class FileApiDriverMemory {
return output; return output;
} }
async delete(path: string) { public async delete(path: string) {
const index = this.itemIndexByPath(path); const index = this.itemIndexByPath(path);
if (index >= 0) { if (index >= 0) {
const item = Object.assign({}, this.items_[index]); const item = Object.assign({}, this.items_[index]);
@ -163,18 +163,18 @@ export default class FileApiDriverMemory {
} }
} }
async move(oldPath: string, newPath: string): Promise<any> { public async move(oldPath: string, newPath: string): Promise<any> {
const sourceItem = this.itemByPath(oldPath); const sourceItem = this.itemByPath(oldPath);
if (!sourceItem) return Promise.reject(new Error(`Path not found: ${oldPath}`)); if (!sourceItem) return Promise.reject(new Error(`Path not found: ${oldPath}`));
await this.delete(newPath); // Overwrite if newPath already exists await this.delete(newPath); // Overwrite if newPath already exists
sourceItem.path = newPath; sourceItem.path = newPath;
} }
async format() { public async format() {
this.items_ = []; this.items_ = [];
} }
async delta(path: string, options: any = null) { public async delta(path: string, options: any = null) {
const getStatFn = async (path: string) => { const getStatFn = async (path: string) => {
const output = this.items_.slice(); const output = this.items_.slice();
for (let i = 0; i < output.length; i++) { for (let i = 0; i < output.length; i++) {
@ -189,7 +189,7 @@ export default class FileApiDriverMemory {
return output; return output;
} }
async clearRoot() { public async clearRoot() {
this.items_ = []; this.items_ = [];
} }
} }

View File

@ -99,13 +99,13 @@ class FileApi {
private remoteDateMutex_ = new Mutex(); private remoteDateMutex_ = new Mutex();
private initialized_ = false; private initialized_ = false;
constructor(baseDir: string | Function, driver: any) { public constructor(baseDir: string | Function, driver: any) {
this.baseDir_ = baseDir; this.baseDir_ = baseDir;
this.driver_ = driver; this.driver_ = driver;
this.driver_.fileApi_ = this; this.driver_.fileApi_ = this;
} }
async initialize() { public async initialize() {
if (this.initialized_) return; if (this.initialized_) return;
this.initialized_ = true; this.initialized_ = true;
if (this.driver_.initialize) return this.driver_.initialize(this.fullPath('')); if (this.driver_.initialize) return this.driver_.initialize(this.fullPath(''));
@ -135,7 +135,7 @@ class FileApi {
return !!this.driver().supportsLocks; return !!this.driver().supportsLocks;
} }
async fetchRemoteDateOffset_() { private async fetchRemoteDateOffset_() {
const tempFile = `${this.tempDirName()}/timeCheck${Math.round(Math.random() * 1000000)}.txt`; const tempFile = `${this.tempDirName()}/timeCheck${Math.round(Math.random() * 1000000)}.txt`;
const startTime = Date.now(); const startTime = Date.now();
await this.put(tempFile, 'timeCheck'); await this.put(tempFile, 'timeCheck');
@ -161,7 +161,7 @@ class FileApi {
// Approximates the current time on the sync target. It caches the time offset to // Approximates the current time on the sync target. It caches the time offset to
// improve performance. // improve performance.
async remoteDate() { public async remoteDate() {
const shouldSyncTime = () => { const shouldSyncTime = () => {
return !this.remoteDateNextCheckTime_ || Date.now() > this.remoteDateNextCheckTime_; return !this.remoteDateNextCheckTime_ || Date.now() > this.remoteDateNextCheckTime_;
}; };
@ -193,60 +193,60 @@ class FileApi {
// Ideally all requests repeating should be done at the FileApi level to remove duplicate code in the drivers, but // Ideally all requests repeating should be done at the FileApi level to remove duplicate code in the drivers, but
// historically some drivers (eg. OneDrive) are already handling request repeating, so this is optional, per driver, // historically some drivers (eg. OneDrive) are already handling request repeating, so this is optional, per driver,
// and it defaults to no repeating. // and it defaults to no repeating.
requestRepeatCount() { public requestRepeatCount() {
if (this.requestRepeatCount_ !== null) return this.requestRepeatCount_; if (this.requestRepeatCount_ !== null) return this.requestRepeatCount_;
if (this.driver_.requestRepeatCount) return this.driver_.requestRepeatCount(); if (this.driver_.requestRepeatCount) return this.driver_.requestRepeatCount();
return 0; return 0;
} }
lastRequests() { public lastRequests() {
return this.driver_.lastRequests ? this.driver_.lastRequests() : []; return this.driver_.lastRequests ? this.driver_.lastRequests() : [];
} }
clearLastRequests() { public clearLastRequests() {
if (this.driver_.clearLastRequests) this.driver_.clearLastRequests(); if (this.driver_.clearLastRequests) this.driver_.clearLastRequests();
} }
baseDir() { public baseDir() {
return typeof this.baseDir_ === 'function' ? this.baseDir_() : this.baseDir_; return typeof this.baseDir_ === 'function' ? this.baseDir_() : this.baseDir_;
} }
tempDirName() { public tempDirName() {
if (this.tempDirName_ === null) throw Error('Temp dir not set!'); if (this.tempDirName_ === null) throw Error('Temp dir not set!');
return this.tempDirName_; return this.tempDirName_;
} }
setTempDirName(v: string) { public setTempDirName(v: string) {
this.tempDirName_ = v; this.tempDirName_ = v;
} }
fsDriver() { public fsDriver() {
return shim.fsDriver(); return shim.fsDriver();
} }
driver() { public driver() {
return this.driver_; return this.driver_;
} }
setSyncTargetId(v: number) { public setSyncTargetId(v: number) {
this.syncTargetId_ = v; this.syncTargetId_ = v;
} }
syncTargetId() { public syncTargetId() {
if (this.syncTargetId_ === null) throw new Error('syncTargetId has not been set!!'); if (this.syncTargetId_ === null) throw new Error('syncTargetId has not been set!!');
return this.syncTargetId_; return this.syncTargetId_;
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
if (!l) l = new Logger(); if (!l) l = new Logger();
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
fullPath(path: string) { public fullPath(path: string) {
const output = []; const output = [];
if (this.baseDir()) output.push(this.baseDir()); if (this.baseDir()) output.push(this.baseDir());
if (path) output.push(path); if (path) output.push(path);
@ -286,18 +286,18 @@ class FileApi {
} }
// Deprectated // Deprectated
setTimestamp(path: string, timestampMs: number) { public setTimestamp(path: string, timestampMs: number) {
logger.debug(`setTimestamp ${this.fullPath(path)}`); logger.debug(`setTimestamp ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.setTimestamp(this.fullPath(path), timestampMs), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.setTimestamp(this.fullPath(path), timestampMs), this.requestRepeatCount());
// return this.driver_.setTimestamp(this.fullPath(path), timestampMs); // return this.driver_.setTimestamp(this.fullPath(path), timestampMs);
} }
mkdir(path: string) { public mkdir(path: string) {
logger.debug(`mkdir ${this.fullPath(path)}`); logger.debug(`mkdir ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.mkdir(this.fullPath(path)), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.mkdir(this.fullPath(path)), this.requestRepeatCount());
} }
async stat(path: string) { public async stat(path: string) {
logger.debug(`stat ${this.fullPath(path)}`); logger.debug(`stat ${this.fullPath(path)}`);
const output = await tryAndRepeat(() => this.driver_.stat(this.fullPath(path)), this.requestRepeatCount()); const output = await tryAndRepeat(() => this.driver_.stat(this.fullPath(path)), this.requestRepeatCount());
@ -308,14 +308,14 @@ class FileApi {
} }
// Returns UTF-8 encoded string by default, or a Response if `options.target = 'file'` // Returns UTF-8 encoded string by default, or a Response if `options.target = 'file'`
get(path: string, options: any = null) { public get(path: string, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!options.encoding) options.encoding = 'utf8'; if (!options.encoding) options.encoding = 'utf8';
logger.debug(`get ${this.fullPath(path)}`); logger.debug(`get ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.get(this.fullPath(path), options), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.get(this.fullPath(path), options), this.requestRepeatCount());
} }
async put(path: string, content: any, options: any = null) { public async put(path: string, content: any, options: any = null) {
logger.debug(`put ${this.fullPath(path)}`, options); logger.debug(`put ${this.fullPath(path)}`, options);
if (options && options.source === 'file') { if (options && options.source === 'file') {
@ -330,27 +330,27 @@ class FileApi {
return tryAndRepeat(() => this.driver_.multiPut(items, options), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.multiPut(items, options), this.requestRepeatCount());
} }
delete(path: string) { public delete(path: string) {
logger.debug(`delete ${this.fullPath(path)}`); logger.debug(`delete ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.delete(this.fullPath(path)), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.delete(this.fullPath(path)), this.requestRepeatCount());
} }
// Deprectated // Deprectated
move(oldPath: string, newPath: string) { public move(oldPath: string, newPath: string) {
logger.debug(`move ${this.fullPath(oldPath)} => ${this.fullPath(newPath)}`); logger.debug(`move ${this.fullPath(oldPath)} => ${this.fullPath(newPath)}`);
return tryAndRepeat(() => this.driver_.move(this.fullPath(oldPath), this.fullPath(newPath)), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.move(this.fullPath(oldPath), this.fullPath(newPath)), this.requestRepeatCount());
} }
// Deprectated // Deprectated
format() { public format() {
return tryAndRepeat(() => this.driver_.format(), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.format(), this.requestRepeatCount());
} }
clearRoot() { public clearRoot() {
return tryAndRepeat(() => this.driver_.clearRoot(this.baseDir()), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.clearRoot(this.baseDir()), this.requestRepeatCount());
} }
delta(path: string, options: any = null) { public delta(path: string, options: any = null) {
logger.debug(`delta ${this.fullPath(path)}`); logger.debug(`delta ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.delta(this.fullPath(path), options), this.requestRepeatCount()); return tryAndRepeat(() => this.driver_.delta(this.fullPath(path), options), this.requestRepeatCount());
} }

View File

@ -58,7 +58,7 @@ const geoipServices: Record<string, GeoipService> = {
}; };
export default class { export default class {
static async currentPosition(options: CurrentPositionOptions = null) { public static async currentPosition(options: CurrentPositionOptions = null) {
if (!options) options = {}; if (!options) options = {};
for (const [serviceName, handler] of Object.entries(geoipServices)) { for (const [serviceName, handler] of Object.entries(geoipServices)) {

View File

@ -10,23 +10,23 @@ export interface Notification {
} }
export default class Alarm extends BaseModel { export default class Alarm extends BaseModel {
static tableName() { public static tableName() {
return 'alarms'; return 'alarms';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_ALARM; return BaseModel.TYPE_ALARM;
} }
static byNoteId(noteId: string) { public static byNoteId(noteId: string) {
return this.modelSelectOne('SELECT * FROM alarms WHERE note_id = ?', [noteId]); return this.modelSelectOne('SELECT * FROM alarms WHERE note_id = ?', [noteId]);
} }
static async deleteExpiredAlarms() { public static async deleteExpiredAlarms() {
return this.db().exec('DELETE FROM alarms WHERE trigger_time <= ?', [Date.now()]); return this.db().exec('DELETE FROM alarms WHERE trigger_time <= ?', [Date.now()]);
} }
static async alarmIdsWithoutNotes() { public static async alarmIdsWithoutNotes() {
// https://stackoverflow.com/a/4967229/561309 // https://stackoverflow.com/a/4967229/561309
const alarms = await this.db().selectAll('SELECT alarms.id FROM alarms LEFT JOIN notes ON alarms.note_id = notes.id WHERE notes.id IS NULL'); const alarms = await this.db().selectAll('SELECT alarms.id FROM alarms LEFT JOIN notes ON alarms.note_id = notes.id WHERE notes.id IS NULL');
return alarms.map((a: any) => { return alarms.map((a: any) => {
@ -34,7 +34,7 @@ export default class Alarm extends BaseModel {
}); });
} }
static async makeNotification(alarm: any, note: any = null): Promise<Notification> { public static async makeNotification(alarm: any, note: any = null): Promise<Notification> {
if (!note) { if (!note) {
note = await Note.load(alarm.note_id); note = await Note.load(alarm.note_id);
} else if (!note.todo_due) { } else if (!note.todo_due) {
@ -55,7 +55,7 @@ export default class Alarm extends BaseModel {
return output; return output;
} }
static async allDue() { public static async allDue() {
return this.modelSelectAll('SELECT * FROM alarms WHERE trigger_time >= ?', [Date.now()]); return this.modelSelectAll('SELECT * FROM alarms WHERE trigger_time >= ?', [Date.now()]);
} }
} }

View File

@ -63,15 +63,15 @@ export default class BaseItem extends BaseModel {
public static SYNC_ITEM_LOCATION_REMOTE = 2; public static SYNC_ITEM_LOCATION_REMOTE = 2;
static useUuid() { public static useUuid() {
return true; return true;
} }
static encryptionSupported() { public static encryptionSupported() {
return true; return true;
} }
static loadClass(className: string, classRef: any) { public static loadClass(className: string, classRef: any) {
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) { for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
if (BaseItem.syncItemDefinitions_[i].className === className) { if (BaseItem.syncItemDefinitions_[i].className === className) {
BaseItem.syncItemDefinitions_[i].classRef = classRef; BaseItem.syncItemDefinitions_[i].classRef = classRef;
@ -82,7 +82,7 @@ export default class BaseItem extends BaseModel {
throw new Error(`Invalid class name: ${className}`); throw new Error(`Invalid class name: ${className}`);
} }
static async findUniqueItemTitle(title: string, parentId: string = null) { public static async findUniqueItemTitle(title: string, parentId: string = null) {
let counter = 1; let counter = 1;
let titleToTry = title; let titleToTry = title;
while (true) { while (true) {
@ -106,7 +106,7 @@ export default class BaseItem extends BaseModel {
} }
// Need to dynamically load the classes like this to avoid circular dependencies // Need to dynamically load the classes like this to avoid circular dependencies
static getClass(name: string) { public static getClass(name: string) {
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) { for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
if (BaseItem.syncItemDefinitions_[i].className === name) { if (BaseItem.syncItemDefinitions_[i].className === name) {
const classRef = BaseItem.syncItemDefinitions_[i].classRef; const classRef = BaseItem.syncItemDefinitions_[i].classRef;
@ -118,7 +118,7 @@ export default class BaseItem extends BaseModel {
throw new Error(`Invalid class name: ${name}`); throw new Error(`Invalid class name: ${name}`);
} }
static getClassByItemType(itemType: ModelType) { public static getClassByItemType(itemType: ModelType) {
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) { for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
if (BaseItem.syncItemDefinitions_[i].type === itemType) { if (BaseItem.syncItemDefinitions_[i].type === itemType) {
return BaseItem.syncItemDefinitions_[i].classRef; return BaseItem.syncItemDefinitions_[i].classRef;
@ -128,7 +128,7 @@ export default class BaseItem extends BaseModel {
throw new Error(`Invalid item type: ${itemType}`); throw new Error(`Invalid item type: ${itemType}`);
} }
static async syncedCount(syncTarget: number) { public static async syncedCount(syncTarget: number) {
const ItemClass = this.itemClass(this.modelType()); const ItemClass = this.itemClass(this.modelType());
const itemType = ItemClass.modelType(); const itemType = ItemClass.modelType();
// The fact that we don't check if the item_id still exist in the corresponding item table, means // The fact that we don't check if the item_id still exist in the corresponding item table, means
@ -145,7 +145,7 @@ export default class BaseItem extends BaseModel {
else return `${itemOrId.id}.${extension}`; else return `${itemOrId.id}.${extension}`;
} }
static isSystemPath(path: string) { public static isSystemPath(path: string) {
// 1b175bb38bba47baac22b0b47f778113.md // 1b175bb38bba47baac22b0b47f778113.md
if (!path || !path.length) return false; if (!path || !path.length) return false;
let p: any = path.split('/'); let p: any = path.split('/');
@ -155,7 +155,7 @@ export default class BaseItem extends BaseModel {
return p[0].length === 32 && p[1] === 'md'; return p[0].length === 32 && p[1] === 'md';
} }
static itemClass(item: any): any { public static itemClass(item: any): any {
if (!item) throw new Error('Item cannot be null'); if (!item) throw new Error('Item cannot be null');
if (typeof item === 'object') { if (typeof item === 'object') {
@ -171,7 +171,7 @@ export default class BaseItem extends BaseModel {
} }
// Returns the IDs of the items that have been synced at least once // Returns the IDs of the items that have been synced at least once
static async syncedItemIds(syncTarget: number) { public static async syncedItemIds(syncTarget: number) {
if (!syncTarget) throw new Error('No syncTarget specified'); if (!syncTarget) throw new Error('No syncTarget specified');
const temp = await this.db().selectAll('SELECT item_id FROM sync_items WHERE sync_time > 0 AND sync_target = ?', [syncTarget]); const temp = await this.db().selectAll('SELECT item_id FROM sync_items WHERE sync_time > 0 AND sync_target = ?', [syncTarget]);
const output = []; const output = [];
@ -181,12 +181,12 @@ export default class BaseItem extends BaseModel {
return output; return output;
} }
static async allSyncItems(syncTarget: number) { public static async allSyncItems(syncTarget: number) {
const output = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_target = ?', [syncTarget]); const output = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_target = ?', [syncTarget]);
return output; return output;
} }
static pathToId(path: string) { public static pathToId(path: string) {
const p = path.split('/'); const p = path.split('/');
const s = p[p.length - 1].split('.'); const s = p[p.length - 1].split('.');
let name: any = s[0]; let name: any = s[0];
@ -195,11 +195,11 @@ export default class BaseItem extends BaseModel {
return name[name.length - 1]; return name[name.length - 1];
} }
static loadItemByPath(path: string) { public static loadItemByPath(path: string) {
return this.loadItemById(this.pathToId(path)); return this.loadItemById(this.pathToId(path));
} }
static async loadItemById(id: string) { public static async loadItemById(id: string) {
const classes = this.syncItemClassNames(); const classes = this.syncItemClassNames();
for (let i = 0; i < classes.length; i++) { for (let i = 0; i < classes.length; i++) {
const item = await this.getClass(classes[i]).load(id); const item = await this.getClass(classes[i]).load(id);
@ -208,7 +208,7 @@ export default class BaseItem extends BaseModel {
return null; return null;
} }
static async loadItemsByIds(ids: string[]) { public static async loadItemsByIds(ids: string[]) {
if (!ids.length) return []; if (!ids.length) return [];
const classes = this.syncItemClassNames(); const classes = this.syncItemClassNames();
@ -222,26 +222,26 @@ export default class BaseItem extends BaseModel {
return output; return output;
} }
static loadItemByField(itemType: number, field: string, value: any) { public static loadItemByField(itemType: number, field: string, value: any) {
const ItemClass = this.itemClass(itemType); const ItemClass = this.itemClass(itemType);
return ItemClass.loadByField(field, value); return ItemClass.loadByField(field, value);
} }
static loadItem(itemType: ModelType, id: string) { public static loadItem(itemType: ModelType, id: string) {
const ItemClass = this.itemClass(itemType); const ItemClass = this.itemClass(itemType);
return ItemClass.load(id); return ItemClass.load(id);
} }
static deleteItem(itemType: ModelType, id: string) { public static deleteItem(itemType: ModelType, id: string) {
const ItemClass = this.itemClass(itemType); const ItemClass = this.itemClass(itemType);
return ItemClass.delete(id); return ItemClass.delete(id);
} }
static async delete(id: string, options: DeleteOptions = null) { public static async delete(id: string, options: DeleteOptions = null) {
return this.batchDelete([id], options); return this.batchDelete([id], options);
} }
static async batchDelete(ids: string[], options: DeleteOptions = null) { public static async batchDelete(ids: string[], options: DeleteOptions = null) {
if (!options) options = {}; if (!options) options = {};
let trackDeleted = true; let trackDeleted = true;
if (options && options.trackDeleted !== null && options.trackDeleted !== undefined) trackDeleted = options.trackDeleted; if (options && options.trackDeleted !== null && options.trackDeleted !== undefined) trackDeleted = options.trackDeleted;
@ -287,20 +287,20 @@ export default class BaseItem extends BaseModel {
// - Client 1 syncs with target 2 only => the note is *not* deleted from target 2 because no information // - Client 1 syncs with target 2 only => the note is *not* deleted from target 2 because no information
// that it was previously deleted exist (deleted_items entry has been deleted). // that it was previously deleted exist (deleted_items entry has been deleted).
// The solution would be to permanently store the list of deleted items on each client. // The solution would be to permanently store the list of deleted items on each client.
static deletedItems(syncTarget: number) { public static deletedItems(syncTarget: number) {
return this.db().selectAll('SELECT * FROM deleted_items WHERE sync_target = ?', [syncTarget]); return this.db().selectAll('SELECT * FROM deleted_items WHERE sync_target = ?', [syncTarget]);
} }
static async deletedItemCount(syncTarget: number) { public static async deletedItemCount(syncTarget: number) {
const r = await this.db().selectOne('SELECT count(*) as total FROM deleted_items WHERE sync_target = ?', [syncTarget]); const r = await this.db().selectOne('SELECT count(*) as total FROM deleted_items WHERE sync_target = ?', [syncTarget]);
return r['total']; return r['total'];
} }
static remoteDeletedItem(syncTarget: number, itemId: string) { public static remoteDeletedItem(syncTarget: number, itemId: string) {
return this.db().exec('DELETE FROM deleted_items WHERE item_id = ? AND sync_target = ?', [itemId, syncTarget]); return this.db().exec('DELETE FROM deleted_items WHERE item_id = ? AND sync_target = ?', [itemId, syncTarget]);
} }
static serialize_format(propName: string, propValue: any) { public static serialize_format(propName: string, propValue: any) {
if (['created_time', 'updated_time', 'sync_time', 'user_updated_time', 'user_created_time'].indexOf(propName) >= 0) { if (['created_time', 'updated_time', 'sync_time', 'user_updated_time', 'user_created_time'].indexOf(propName) >= 0) {
if (!propValue) return ''; if (!propValue) return '';
propValue = `${moment.unix(propValue / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`; propValue = `${moment.unix(propValue / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS')}Z`;
@ -322,7 +322,7 @@ export default class BaseItem extends BaseModel {
.replace(/\r/g, '\\r'); .replace(/\r/g, '\\r');
} }
static unserialize_format(type: ModelType, propName: string, propValue: any) { public static unserialize_format(type: ModelType, propName: string, propValue: any) {
if (propName[propName.length - 1] === '_') return propValue; // Private property if (propName[propName.length - 1] === '_') return propValue; // Private property
const ItemClass = this.itemClass(type); const ItemClass = this.itemClass(type);
@ -350,7 +350,7 @@ export default class BaseItem extends BaseModel {
: propValue; : propValue;
} }
static async serialize(item: any, shownKeys: any[] = null) { public static async serialize(item: any, shownKeys: any[] = null) {
if (shownKeys === null) { if (shownKeys === null) {
shownKeys = this.itemClass(item).fieldNames(); shownKeys = this.itemClass(item).fieldNames();
shownKeys.push('type_'); shownKeys.push('type_');
@ -395,12 +395,12 @@ export default class BaseItem extends BaseModel {
return temp.join('\n\n'); return temp.join('\n\n');
} }
static encryptionService() { public static encryptionService() {
if (!this.encryptionService_) throw new Error('BaseItem.encryptionService_ is not set!!'); if (!this.encryptionService_) throw new Error('BaseItem.encryptionService_ is not set!!');
return this.encryptionService_; return this.encryptionService_;
} }
static revisionService() { public static revisionService() {
if (!this.revisionService_) throw new Error('BaseItem.revisionService_ is not set!!'); if (!this.revisionService_) throw new Error('BaseItem.revisionService_ is not set!!');
return this.revisionService_; return this.revisionService_;
} }
@ -460,7 +460,7 @@ export default class BaseItem extends BaseModel {
return ItemClass.serialize(reducedItem); return ItemClass.serialize(reducedItem);
} }
static async decrypt(item: any) { public static async decrypt(item: any) {
if (!item.encryption_cipher_text) throw new Error(`Item is not encrypted: ${item.id}`); if (!item.encryption_cipher_text) throw new Error(`Item is not encrypted: ${item.id}`);
const ItemClass = this.itemClass(item); const ItemClass = this.itemClass(item);
@ -474,7 +474,7 @@ export default class BaseItem extends BaseModel {
return ItemClass.save(plainItem, { autoTimestamp: false, changeSource: ItemChange.SOURCE_DECRYPTION }); return ItemClass.save(plainItem, { autoTimestamp: false, changeSource: ItemChange.SOURCE_DECRYPTION });
} }
static async unserialize(content: string) { public static async unserialize(content: string) {
const lines = content.split('\n'); const lines = content.split('\n');
let output: any = {}; let output: any = {};
let state = 'readingProps'; let state = 'readingProps';
@ -539,7 +539,7 @@ export default class BaseItem extends BaseModel {
}; };
} }
static async encryptedItemsCount() { public static async encryptedItemsCount() {
const classNames = this.encryptableItemClassNames(); const classNames = this.encryptableItemClassNames();
let output = 0; let output = 0;
@ -553,7 +553,7 @@ export default class BaseItem extends BaseModel {
return output; return output;
} }
static async hasEncryptedItems() { public static async hasEncryptedItems() {
const classNames = this.encryptableItemClassNames(); const classNames = this.encryptableItemClassNames();
for (let i = 0; i < classNames.length; i++) { for (let i = 0; i < classNames.length; i++) {
@ -567,7 +567,7 @@ export default class BaseItem extends BaseModel {
return false; return false;
} }
static async itemsThatNeedDecryption(exclusions: string[] = [], limit = 100): Promise<ItemsThatNeedDecryptionResult> { public static async itemsThatNeedDecryption(exclusions: string[] = [], limit = 100): Promise<ItemsThatNeedDecryptionResult> {
const classNames = this.encryptableItemClassNames(); const classNames = this.encryptableItemClassNames();
for (let i = 0; i < classNames.length; i++) { for (let i = 0; i < classNames.length; i++) {
@ -703,13 +703,13 @@ export default class BaseItem extends BaseModel {
throw new Error('Unreachable'); throw new Error('Unreachable');
} }
static syncItemClassNames(): string[] { public static syncItemClassNames(): string[] {
return BaseItem.syncItemDefinitions_.map((def: any) => { return BaseItem.syncItemDefinitions_.map((def: any) => {
return def.className; return def.className;
}); });
} }
static encryptableItemClassNames() { public static encryptableItemClassNames() {
const temp = this.syncItemClassNames(); const temp = this.syncItemClassNames();
const output = []; const output = [];
for (let i = 0; i < temp.length; i++) { for (let i = 0; i < temp.length; i++) {
@ -725,14 +725,14 @@ export default class BaseItem extends BaseModel {
}); });
} }
static modelTypeToClassName(type: number) { public static modelTypeToClassName(type: number) {
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) { for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
if (BaseItem.syncItemDefinitions_[i].type === type) return BaseItem.syncItemDefinitions_[i].className; if (BaseItem.syncItemDefinitions_[i].type === type) return BaseItem.syncItemDefinitions_[i].className;
} }
throw new Error(`Invalid type: ${type}`); throw new Error(`Invalid type: ${type}`);
} }
static async syncDisabledItems(syncTargetId: number) { public static async syncDisabledItems(syncTargetId: number) {
const rows = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_disabled = 1 AND sync_target = ?', [syncTargetId]); const rows = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_disabled = 1 AND sync_target = ?', [syncTargetId]);
const output = []; const output = [];
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@ -749,7 +749,7 @@ export default class BaseItem extends BaseModel {
return output; return output;
} }
static updateSyncTimeQueries(syncTarget: number, item: any, syncTime: number, syncDisabled = false, syncDisabledReason = '', itemLocation: number = null) { public static updateSyncTimeQueries(syncTarget: number, item: any, syncTime: number, syncDisabled = false, syncDisabledReason = '', itemLocation: number = null) {
const itemType = item.type_; const itemType = item.type_;
const itemId = item.id; const itemId = item.id;
if (!itemType || !itemId || syncTime === undefined) throw new Error(sprintf('Invalid parameters in updateSyncTimeQueries(): %d, %s, %d', syncTarget, JSON.stringify(item), syncTime)); if (!itemType || !itemId || syncTime === undefined) throw new Error(sprintf('Invalid parameters in updateSyncTimeQueries(): %d, %s, %d', syncTarget, JSON.stringify(item), syncTime));
@ -768,12 +768,12 @@ export default class BaseItem extends BaseModel {
]; ];
} }
static async saveSyncTime(syncTarget: number, item: any, syncTime: number) { public static async saveSyncTime(syncTarget: number, item: any, syncTime: number) {
const queries = this.updateSyncTimeQueries(syncTarget, item, syncTime); const queries = this.updateSyncTimeQueries(syncTarget, item, syncTime);
return this.db().transactionExecBatch(queries); return this.db().transactionExecBatch(queries);
} }
static async saveSyncDisabled(syncTargetId: number, item: any, syncDisabledReason: string, itemLocation: number = null) { public static async saveSyncDisabled(syncTargetId: number, item: any, syncDisabledReason: string, itemLocation: number = null) {
const syncTime = 'sync_time' in item ? item.sync_time : 0; const syncTime = 'sync_time' in item ? item.sync_time : 0;
const queries = this.updateSyncTimeQueries(syncTargetId, item, syncTime, true, syncDisabledReason, itemLocation); const queries = this.updateSyncTimeQueries(syncTargetId, item, syncTime, true, syncDisabledReason, itemLocation);
return this.db().transactionExecBatch(queries); return this.db().transactionExecBatch(queries);
@ -786,7 +786,7 @@ export default class BaseItem extends BaseModel {
// When an item is deleted, its associated sync_items data is not immediately deleted for // When an item is deleted, its associated sync_items data is not immediately deleted for
// performance reason. So this function is used to look for these remaining sync_items and // performance reason. So this function is used to look for these remaining sync_items and
// delete them. // delete them.
static async deleteOrphanSyncItems() { public static async deleteOrphanSyncItems() {
const classNames = this.syncItemClassNames(); const classNames = this.syncItemClassNames();
const queries = []; const queries = [];
@ -803,13 +803,13 @@ export default class BaseItem extends BaseModel {
await this.db().transactionExecBatch(queries); await this.db().transactionExecBatch(queries);
} }
static displayTitle(item: any) { public static displayTitle(item: any) {
if (!item) return ''; if (!item) return '';
if (item.encryption_applied) return `🔑 ${_('Encrypted')}`; if (item.encryption_applied) return `🔑 ${_('Encrypted')}`;
return item.title ? item.title : _('Untitled'); return item.title ? item.title : _('Untitled');
} }
static async markAllNonEncryptedForSync() { public static async markAllNonEncryptedForSync() {
const classNames = this.encryptableItemClassNames(); const classNames = this.encryptableItemClassNames();
for (let i = 0; i < classNames.length; i++) { for (let i = 0; i < classNames.length; i++) {
@ -834,7 +834,7 @@ export default class BaseItem extends BaseModel {
} }
} }
static async updateShareStatus(item: BaseItemEntity, isShared: boolean) { public static async updateShareStatus(item: BaseItemEntity, isShared: boolean) {
if (!item.id || !item.type_) throw new Error('Item must have an ID and a type'); if (!item.id || !item.type_) throw new Error('Item must have an ID and a type');
if (!!item.is_shared === !!isShared) return false; if (!!item.is_shared === !!isShared) return false;
const ItemClass = this.getClassByItemType(item.type_); const ItemClass = this.getClassByItemType(item.type_);
@ -853,15 +853,15 @@ export default class BaseItem extends BaseModel {
return true; return true;
} }
static async forceSync(itemId: string) { public static async forceSync(itemId: string) {
await this.db().exec('UPDATE sync_items SET force_sync = 1 WHERE item_id = ?', [itemId]); await this.db().exec('UPDATE sync_items SET force_sync = 1 WHERE item_id = ?', [itemId]);
} }
static async forceSyncAll() { public static async forceSyncAll() {
await this.db().exec('UPDATE sync_items SET force_sync = 1'); await this.db().exec('UPDATE sync_items SET force_sync = 1');
} }
static async save(o: any, options: any = null) { public static async save(o: any, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (options.userSideValidation === true) { if (options.userSideValidation === true) {
@ -871,7 +871,7 @@ export default class BaseItem extends BaseModel {
return super.save(o, options); return super.save(o, options);
} }
static markdownTag(itemOrId: any) { public static markdownTag(itemOrId: any) {
const item = typeof itemOrId === 'object' ? itemOrId : { const item = typeof itemOrId === 'object' ? itemOrId : {
id: itemOrId, id: itemOrId,
title: '', title: '',
@ -885,7 +885,7 @@ export default class BaseItem extends BaseModel {
return output.join(''); return output.join('');
} }
static isMarkdownTag(md: any) { public static isMarkdownTag(md: any) {
if (!md) return false; if (!md) return false;
return !!md.match(/^\[.*?\]\(:\/[0-9a-zA-Z]{32}\)$/); return !!md.match(/^\[.*?\]\(:\/[0-9a-zA-Z]{32}\)$/);
} }

View File

@ -19,22 +19,22 @@ export interface FolderEntityWithChildren extends FolderEntity {
} }
export default class Folder extends BaseItem { export default class Folder extends BaseItem {
static tableName() { public static tableName() {
return 'folders'; return 'folders';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_FOLDER; return BaseModel.TYPE_FOLDER;
} }
static newFolder(): FolderEntity { public static newFolder(): FolderEntity {
return { return {
id: null, id: null,
title: '', title: '',
}; };
} }
static fieldToLabel(field: string) { public static fieldToLabel(field: string) {
const fieldsToLabels: any = { const fieldsToLabels: any = {
title: _('title'), title: _('title'),
last_note_user_updated_time: _('updated date'), last_note_user_updated_time: _('updated date'),
@ -43,7 +43,7 @@ export default class Folder extends BaseItem {
return field in fieldsToLabels ? fieldsToLabels[field] : field; return field in fieldsToLabels ? fieldsToLabels[field] : field;
} }
static noteIds(parentId: string, options: any = null) { public static noteIds(parentId: string, options: any = null) {
options = Object.assign({}, { options = Object.assign({}, {
includeConflicts: false, includeConflicts: false,
}, options); }, options);
@ -66,17 +66,17 @@ export default class Folder extends BaseItem {
}); });
} }
static async subFolderIds(parentId: string) { public static async subFolderIds(parentId: string) {
const rows = await this.db().selectAll('SELECT id FROM folders WHERE parent_id = ?', [parentId]); const rows = await this.db().selectAll('SELECT id FROM folders WHERE parent_id = ?', [parentId]);
return rows.map((r: FolderEntity) => r.id); return rows.map((r: FolderEntity) => r.id);
} }
static async noteCount(parentId: string) { public static async noteCount(parentId: string) {
const r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]); const r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]);
return r ? r.total : 0; return r ? r.total : 0;
} }
static markNotesAsConflict(parentId: string) { public static markNotesAsConflict(parentId: string) {
const query = Database.updateQuery('notes', { is_conflict: 1 }, { parent_id: parentId }); const query = Database.updateQuery('notes', { is_conflict: 1 }, { parent_id: parentId });
return this.db().exec(query); return this.db().exec(query);
} }
@ -108,15 +108,15 @@ export default class Folder extends BaseItem {
}); });
} }
static conflictFolderTitle() { public static conflictFolderTitle() {
return _('Conflicts'); return _('Conflicts');
} }
static conflictFolderId() { public static conflictFolderId() {
return 'c04f1c7c04f1c7c04f1c7c04f1c7c04f'; return 'c04f1c7c04f1c7c04f1c7c04f1c7c04f';
} }
static conflictFolder(): FolderEntity { public static conflictFolder(): FolderEntity {
return { return {
type_: this.TYPE_FOLDER, type_: this.TYPE_FOLDER,
id: this.conflictFolderId(), id: this.conflictFolderId(),
@ -129,7 +129,7 @@ export default class Folder extends BaseItem {
// Calculates note counts for all folders and adds the note_count attribute to each folder // Calculates note counts for all folders and adds the note_count attribute to each folder
// Note: this only calculates the overall number of nodes for this folder and all its descendants // Note: this only calculates the overall number of nodes for this folder and all its descendants
static async addNoteCounts(folders: any[], includeCompletedTodos = true) { public static async addNoteCounts(folders: any[], includeCompletedTodos = true) {
const foldersById: any = {}; const foldersById: any = {};
for (const f of folders) { for (const f of folders) {
foldersById[f.id] = f; foldersById[f.id] = f;
@ -170,7 +170,7 @@ export default class Folder extends BaseItem {
// Folders that contain notes that have been modified recently go on top. // Folders that contain notes that have been modified recently go on top.
// The remaining folders, that don't contain any notes are sorted by their own user_updated_time // The remaining folders, that don't contain any notes are sorted by their own user_updated_time
static async orderByLastModified(folders: FolderEntity[], dir = 'DESC') { public static async orderByLastModified(folders: FolderEntity[], dir = 'DESC') {
dir = dir.toUpperCase(); dir = dir.toUpperCase();
const sql = 'select parent_id, max(user_updated_time) content_updated_time from notes where parent_id != "" group by parent_id'; const sql = 'select parent_id, max(user_updated_time) content_updated_time from notes where parent_id != "" group by parent_id';
const rows = await this.db().selectAll(sql); const rows = await this.db().selectAll(sql);
@ -228,7 +228,7 @@ export default class Folder extends BaseItem {
return output; return output;
} }
static async all(options: any = null) { public static async all(options: any = null) {
const output = await super.all(options); const output = await super.all(options);
if (options && options.includeConflictFolder) { if (options && options.includeConflictFolder) {
const conflictCount = await Note.conflictedCount(); const conflictCount = await Note.conflictedCount();
@ -237,7 +237,7 @@ export default class Folder extends BaseItem {
return output; return output;
} }
static async childrenIds(folderId: string) { public static async childrenIds(folderId: string) {
const folders = await this.db().selectAll('SELECT id FROM folders WHERE parent_id = ?', [folderId]); const folders = await this.db().selectAll('SELECT id FROM folders WHERE parent_id = ?', [folderId]);
let output: string[] = []; let output: string[] = [];
@ -252,7 +252,7 @@ export default class Folder extends BaseItem {
return output; return output;
} }
static async expandTree(folders: FolderEntity[], parentId: string) { public static async expandTree(folders: FolderEntity[], parentId: string) {
const folderPath = await this.folderPath(folders, parentId); const folderPath = await this.folderPath(folders, parentId);
folderPath.pop(); // We don't expand the leaft notebook folderPath.pop(); // We don't expand the leaft notebook
@ -542,7 +542,7 @@ export default class Folder extends BaseItem {
logger.debug('updateNoLongerSharedItems:', report); logger.debug('updateNoLongerSharedItems:', report);
} }
static async allAsTree(folders: FolderEntity[] = null, options: any = null) { public static async allAsTree(folders: FolderEntity[] = null, options: any = null) {
const all = folders ? folders : await this.all(options); const all = folders ? folders : await this.all(options);
if (options && options.includeNotes) { if (options && options.includeNotes) {
@ -576,7 +576,7 @@ export default class Folder extends BaseItem {
return getNestedChildren(all, ''); return getNestedChildren(all, '');
} }
static folderPath(folders: FolderEntity[], folderId: string) { public static folderPath(folders: FolderEntity[], folderId: string) {
const idToFolders: Record<string, FolderEntity> = {}; const idToFolders: Record<string, FolderEntity> = {};
for (let i = 0; i < folders.length; i++) { for (let i = 0; i < folders.length; i++) {
idToFolders[folders[i].id] = folders[i]; idToFolders[folders[i].id] = folders[i];
@ -595,7 +595,7 @@ export default class Folder extends BaseItem {
return path; return path;
} }
static folderPathString(folders: FolderEntity[], folderId: string, maxTotalLength = 80) { public static folderPathString(folders: FolderEntity[], folderId: string, maxTotalLength = 80) {
const path = this.folderPath(folders, folderId); const path = this.folderPath(folders, folderId);
let currentTotalLength = 0; let currentTotalLength = 0;
@ -616,7 +616,7 @@ export default class Folder extends BaseItem {
return output.join(' / '); return output.join(' / ');
} }
static buildTree(folders: FolderEntity[]): FolderEntityWithChildren[] { public static buildTree(folders: FolderEntity[]): FolderEntityWithChildren[] {
const idToFolders: Record<string, any> = {}; const idToFolders: Record<string, any> = {};
for (let i = 0; i < folders.length; i++) { for (let i = 0; i < folders.length; i++) {
idToFolders[folders[i].id] = Object.assign({}, folders[i]); idToFolders[folders[i].id] = Object.assign({}, folders[i]);
@ -644,7 +644,7 @@ export default class Folder extends BaseItem {
return rootFolders; return rootFolders;
} }
static async sortFolderTree(folders: FolderEntityWithChildren[] = null) { public static async sortFolderTree(folders: FolderEntityWithChildren[] = null) {
const output = folders ? folders : await this.allAsTree(); const output = folders ? folders : await this.allAsTree();
const sortFoldersAlphabetically = (folders: FolderEntityWithChildren[]) => { const sortFoldersAlphabetically = (folders: FolderEntityWithChildren[]) => {
@ -672,16 +672,16 @@ export default class Folder extends BaseItem {
return output; return output;
} }
static load(id: string, _options: any = null): Promise<FolderEntity> { public static load(id: string, _options: any = null): Promise<FolderEntity> {
if (id === this.conflictFolderId()) return Promise.resolve(this.conflictFolder()); if (id === this.conflictFolderId()) return Promise.resolve(this.conflictFolder());
return super.load(id); return super.load(id);
} }
static defaultFolder() { public static defaultFolder() {
return this.modelSelectOne('SELECT * FROM folders ORDER BY created_time DESC LIMIT 1'); return this.modelSelectOne('SELECT * FROM folders ORDER BY created_time DESC LIMIT 1');
} }
static async canNestUnder(folderId: string, targetFolderId: string) { public static async canNestUnder(folderId: string, targetFolderId: string) {
if (folderId === targetFolderId) return false; if (folderId === targetFolderId) return false;
const folder = await Folder.load(folderId); const folder = await Folder.load(folderId);
@ -702,7 +702,7 @@ export default class Folder extends BaseItem {
return true; return true;
} }
static async moveToFolder(folderId: string, targetFolderId: string) { public static async moveToFolder(folderId: string, targetFolderId: string) {
if (!(await this.canNestUnder(folderId, targetFolderId))) throw new Error(_('Cannot move notebook to this location')); if (!(await this.canNestUnder(folderId, targetFolderId))) throw new Error(_('Cannot move notebook to this location'));
// When moving a note to a different folder, the user timestamp is not updated. // When moving a note to a different folder, the user timestamp is not updated.
@ -721,7 +721,7 @@ export default class Folder extends BaseItem {
// manually creating a folder. They shouldn't be done for example when the folders // manually creating a folder. They shouldn't be done for example when the folders
// are being synced to avoid any strange side-effects. Technically it's possible to // are being synced to avoid any strange side-effects. Technically it's possible to
// have folders and notes with duplicate titles (or no title), or with reserved words. // have folders and notes with duplicate titles (or no title), or with reserved words.
static async save(o: FolderEntity, options: any = null) { public static async save(o: FolderEntity, options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (options.userSideValidation === true) { if (options.userSideValidation === true) {

View File

@ -22,11 +22,11 @@ export default class ItemChange extends BaseModel {
public static SOURCE_SYNC = 2; public static SOURCE_SYNC = 2;
public static SOURCE_DECRYPTION = 2; // CAREFUL - SAME ID AS SOURCE_SYNC! public static SOURCE_DECRYPTION = 2; // CAREFUL - SAME ID AS SOURCE_SYNC!
static tableName() { public static tableName() {
return 'item_changes'; return 'item_changes';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_ITEM_CHANGE; return BaseModel.TYPE_ITEM_CHANGE;
} }

View File

@ -5,15 +5,15 @@ import BaseItem from './BaseItem';
import uuid from '../uuid'; import uuid from '../uuid';
export default class MasterKey extends BaseItem { export default class MasterKey extends BaseItem {
static tableName() { public static tableName() {
return 'master_keys'; return 'master_keys';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_MASTER_KEY; return BaseModel.TYPE_MASTER_KEY;
} }
static encryptionSupported() { public static encryptionSupported() {
return false; return false;
} }
@ -28,7 +28,7 @@ export default class MasterKey extends BaseItem {
return output; return output;
} }
static allWithoutEncryptionMethod(masterKeys: MasterKeyEntity[], methods: number[]) { public static allWithoutEncryptionMethod(masterKeys: MasterKeyEntity[], methods: number[]) {
return masterKeys.filter(m => !methods.includes(m.encryption_method)); return masterKeys.filter(m => !methods.includes(m.encryption_method));
} }

View File

@ -10,19 +10,19 @@ const migrationScripts: Record<number, any> = {
}; };
export default class Migration extends BaseModel { export default class Migration extends BaseModel {
static tableName() { public static tableName() {
return 'migrations'; return 'migrations';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_MIGRATION; return BaseModel.TYPE_MIGRATION;
} }
static migrationsToDo() { public static migrationsToDo() {
return this.modelSelectAll('SELECT * FROM migrations ORDER BY number ASC'); return this.modelSelectAll('SELECT * FROM migrations ORDER BY number ASC');
} }
static script(number: number) { public static script(number: number) {
if (!migrationScripts[number]) throw new Error('Migration script has not been added to "migrationScripts" array'); if (!migrationScripts[number]) throw new Error('Migration script has not been added to "migrationScripts" array');
return migrationScripts[number]; return migrationScripts[number];
} }

View File

@ -26,11 +26,11 @@ export default class Note extends BaseItem {
private static geolocationCache_: any; private static geolocationCache_: any;
private static dueDateObjects_: any; private static dueDateObjects_: any;
static tableName() { public static tableName() {
return 'notes'; return 'notes';
} }
static fieldToLabel(field: string) { public static fieldToLabel(field: string) {
const fieldsToLabels: Record<string, string> = { const fieldsToLabels: Record<string, string> = {
title: _('title'), title: _('title'),
user_updated_time: _('updated date'), user_updated_time: _('updated date'),
@ -41,11 +41,11 @@ export default class Note extends BaseItem {
return field in fieldsToLabels ? fieldsToLabels[field] : field; return field in fieldsToLabels ? fieldsToLabels[field] : field;
} }
static async serializeForEdit(note: NoteEntity) { public static async serializeForEdit(note: NoteEntity) {
return this.replaceResourceInternalToExternalLinks(await super.serialize(note, ['title', 'body'])); return this.replaceResourceInternalToExternalLinks(await super.serialize(note, ['title', 'body']));
} }
static async unserializeForEdit(content: string) { public static async unserializeForEdit(content: string) {
content += `\n\ntype_: ${BaseModel.TYPE_NOTE}`; content += `\n\ntype_: ${BaseModel.TYPE_NOTE}`;
const output = await super.unserialize(content); const output = await super.unserialize(content);
if (!output.title) output.title = ''; if (!output.title) output.title = '';
@ -54,14 +54,14 @@ export default class Note extends BaseItem {
return output; return output;
} }
static async serializeAllProps(note: NoteEntity) { public static async serializeAllProps(note: NoteEntity) {
const fieldNames = this.fieldNames(); const fieldNames = this.fieldNames();
fieldNames.push('type_'); fieldNames.push('type_');
pull(fieldNames, 'title', 'body'); pull(fieldNames, 'title', 'body');
return super.serialize(note, fieldNames); return super.serialize(note, fieldNames);
} }
static minimalSerializeForDisplay(note: NoteEntity) { public static minimalSerializeForDisplay(note: NoteEntity) {
const n = Object.assign({}, note); const n = Object.assign({}, note);
const fieldNames = this.fieldNames(); const fieldNames = this.fieldNames();
@ -89,25 +89,25 @@ export default class Note extends BaseItem {
return super.serialize(n, fieldNames); return super.serialize(n, fieldNames);
} }
static defaultTitle(noteBody: string) { public static defaultTitle(noteBody: string) {
return this.defaultTitleFromBody(noteBody); return this.defaultTitleFromBody(noteBody);
} }
static defaultTitleFromBody(body: string) { public static defaultTitleFromBody(body: string) {
return markdownUtils.titleFromBody(body); return markdownUtils.titleFromBody(body);
} }
static geolocationUrl(note: NoteEntity) { public static geolocationUrl(note: NoteEntity) {
if (!('latitude' in note) || !('longitude' in note)) throw new Error('Latitude or longitude is missing'); if (!('latitude' in note) || !('longitude' in note)) throw new Error('Latitude or longitude is missing');
if (!Number(note.latitude) && !Number(note.longitude)) throw new Error(_('This note does not have geolocation information.')); if (!Number(note.latitude) && !Number(note.longitude)) throw new Error(_('This note does not have geolocation information.'));
return this.geoLocationUrlFromLatLong(note.latitude, note.longitude); return this.geoLocationUrlFromLatLong(note.latitude, note.longitude);
} }
static geoLocationUrlFromLatLong(lat: number, long: number) { public static geoLocationUrlFromLatLong(lat: number, long: number) {
return sprintf('https://www.openstreetmap.org/?lat=%s&lon=%s&zoom=20', lat, long); return sprintf('https://www.openstreetmap.org/?lat=%s&lon=%s&zoom=20', lat, long);
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_NOTE; return BaseModel.TYPE_NOTE;
} }
@ -119,13 +119,13 @@ export default class Note extends BaseItem {
return unique(itemIds); return unique(itemIds);
} }
static async linkedItems(body: string) { public static async linkedItems(body: string) {
const itemIds = this.linkedItemIds(body); const itemIds = this.linkedItemIds(body);
const r = await BaseItem.loadItemsByIds(itemIds); const r = await BaseItem.loadItemsByIds(itemIds);
return r; return r;
} }
static async linkedItemIdsByType(type: ModelType, body: string) { public static async linkedItemIdsByType(type: ModelType, body: string) {
const items = await this.linkedItems(body); const items = await this.linkedItems(body);
const output: string[] = []; const output: string[] = [];
@ -137,15 +137,15 @@ export default class Note extends BaseItem {
return output; return output;
} }
static async linkedResourceIds(body: string) { public static async linkedResourceIds(body: string) {
return this.linkedItemIdsByType(BaseModel.TYPE_RESOURCE, body); return this.linkedItemIdsByType(BaseModel.TYPE_RESOURCE, body);
} }
static async linkedNoteIds(body: string) { public static async linkedNoteIds(body: string) {
return this.linkedItemIdsByType(BaseModel.TYPE_NOTE, body); return this.linkedItemIdsByType(BaseModel.TYPE_NOTE, body);
} }
static async replaceResourceInternalToExternalLinks(body: string, options: any = null) { public static async replaceResourceInternalToExternalLinks(body: string, options: any = null) {
options = Object.assign({}, { options = Object.assign({}, {
useAbsolutePaths: false, useAbsolutePaths: false,
}, options); }, options);
@ -175,7 +175,7 @@ export default class Note extends BaseItem {
return body; return body;
} }
static async replaceResourceExternalToInternalLinks(body: string, options: any = null) { public static async replaceResourceExternalToInternalLinks(body: string, options: any = null) {
options = Object.assign({}, { options = Object.assign({}, {
useAbsolutePaths: false, useAbsolutePaths: false,
}, options); }, options);
@ -239,20 +239,20 @@ export default class Note extends BaseItem {
return body; return body;
} }
static new(parentId = '') { public static new(parentId = '') {
const output = super.new(); const output = super.new();
output.parent_id = parentId; output.parent_id = parentId;
return output; return output;
} }
static newTodo(parentId = '') { public static newTodo(parentId = '') {
const output = this.new(parentId); const output = this.new(parentId);
output.is_todo = true; output.is_todo = true;
return output; return output;
} }
// Note: sort logic must be duplicated in previews(). // Note: sort logic must be duplicated in previews().
static sortNotes(notes: NoteEntity[], orders: any[], uncompletedTodosOnTop: boolean) { public static sortNotes(notes: NoteEntity[], orders: any[], uncompletedTodosOnTop: boolean) {
const noteOnTop = (note: NoteEntity) => { const noteOnTop = (note: NoteEntity) => {
return uncompletedTodosOnTop && note.is_todo && !note.todo_completed; return uncompletedTodosOnTop && note.is_todo && !note.todo_completed;
}; };
@ -308,11 +308,11 @@ export default class Note extends BaseItem {
}); });
} }
static previewFieldsWithDefaultValues(options: any = null) { public static previewFieldsWithDefaultValues(options: any = null) {
return Note.defaultValues(this.previewFields(options)); return Note.defaultValues(this.previewFields(options));
} }
static previewFields(options: any = null) { public static previewFields(options: any = null) {
options = Object.assign({ options = Object.assign({
includeTimestamps: true, includeTimestamps: true,
}, options); }, options);
@ -328,13 +328,13 @@ export default class Note extends BaseItem {
return output; return output;
} }
static previewFieldsSql(fields: string[] = null) { public static previewFieldsSql(fields: string[] = null) {
if (fields === null) fields = this.previewFields(); if (fields === null) fields = this.previewFields();
const escaped = this.db().escapeFields(fields); const escaped = this.db().escapeFields(fields);
return Array.isArray(escaped) ? escaped.join(',') : escaped; return Array.isArray(escaped) ? escaped.join(',') : escaped;
} }
static async loadFolderNoteByField(folderId: string, field: string, value: any) { public static async loadFolderNoteByField(folderId: string, field: string, value: any) {
if (!folderId) throw new Error('folderId is undefined'); if (!folderId) throw new Error('folderId is undefined');
const options = { const options = {
@ -347,7 +347,7 @@ export default class Note extends BaseItem {
return results.length ? results[0] : null; return results.length ? results[0] : null;
} }
static async previews(parentId: string, options: any = null) { public static async previews(parentId: string, options: any = null) {
// Note: ordering logic must be duplicated in sortNotes(), which is used // Note: ordering logic must be duplicated in sortNotes(), which is used
// to sort already loaded notes. // to sort already loaded notes.
@ -436,12 +436,12 @@ export default class Note extends BaseItem {
return results; return results;
} }
static preview(noteId: string, options: any = null) { public static preview(noteId: string, options: any = null) {
if (!options) options = { fields: null }; if (!options) options = { fields: null };
return this.modelSelectOne(`SELECT ${this.previewFieldsSql(options.fields)} FROM notes WHERE is_conflict = 0 AND id = ?`, [noteId]); return this.modelSelectOne(`SELECT ${this.previewFieldsSql(options.fields)} FROM notes WHERE is_conflict = 0 AND id = ?`, [noteId]);
} }
static async search(options: any = null) { public static async search(options: any = null) {
if (!options) options = {}; if (!options) options = {};
if (!options.conditions) options.conditions = []; if (!options.conditions) options.conditions = [];
if (!options.conditionsParams) options.conditionsParams = []; if (!options.conditionsParams) options.conditionsParams = [];
@ -455,16 +455,16 @@ export default class Note extends BaseItem {
return super.search(options); return super.search(options);
} }
static conflictedNotes() { public static conflictedNotes() {
return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 1'); return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 1');
} }
static async conflictedCount() { public static async conflictedCount() {
const r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 1'); const r = await this.db().selectOne('SELECT count(*) as total FROM notes WHERE is_conflict = 1');
return r && r.total ? r.total : 0; return r && r.total ? r.total : 0;
} }
static unconflictedNotes() { public static unconflictedNotes() {
return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0'); return this.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0');
} }
@ -518,7 +518,7 @@ export default class Note extends BaseItem {
return note; return note;
} }
static filter(note: NoteEntity) { public static filter(note: NoteEntity) {
if (!note) return note; if (!note) return note;
const output = super.filter(note); const output = super.filter(note);
@ -528,7 +528,7 @@ export default class Note extends BaseItem {
return output; return output;
} }
static async copyToFolder(noteId: string, folderId: string) { public static async copyToFolder(noteId: string, folderId: string) {
if (folderId === this.getClass('Folder').conflictFolderId()) throw new Error(_('Cannot copy note to "%s" notebook', this.getClass('Folder').conflictFolderTitle())); if (folderId === this.getClass('Folder').conflictFolderId()) throw new Error(_('Cannot copy note to "%s" notebook', this.getClass('Folder').conflictFolderTitle()));
return Note.duplicate(noteId, { return Note.duplicate(noteId, {
@ -540,7 +540,7 @@ export default class Note extends BaseItem {
}); });
} }
static async moveToFolder(noteId: string, folderId: string) { public static async moveToFolder(noteId: string, folderId: string) {
if (folderId === this.getClass('Folder').conflictFolderId()) throw new Error(_('Cannot move note to "%s" notebook', this.getClass('Folder').conflictFolderTitle())); if (folderId === this.getClass('Folder').conflictFolderId()) throw new Error(_('Cannot move note to "%s" notebook', this.getClass('Folder').conflictFolderTitle()));
// When moving a note to a different folder, the user timestamp is not updated. // When moving a note to a different folder, the user timestamp is not updated.
@ -557,7 +557,7 @@ export default class Note extends BaseItem {
return Note.save(modifiedNote, { autoTimestamp: false }); return Note.save(modifiedNote, { autoTimestamp: false });
} }
static changeNoteType(note: NoteEntity, type: string) { public static changeNoteType(note: NoteEntity, type: string) {
if (!('is_todo' in note)) throw new Error('Missing "is_todo" property'); if (!('is_todo' in note)) throw new Error('Missing "is_todo" property');
const newIsTodo = type === 'todo' ? 1 : 0; const newIsTodo = type === 'todo' ? 1 : 0;
@ -572,11 +572,11 @@ export default class Note extends BaseItem {
return output; return output;
} }
static toggleIsTodo(note: NoteEntity) { public static toggleIsTodo(note: NoteEntity) {
return this.changeNoteType(note, note.is_todo ? 'note' : 'todo'); return this.changeNoteType(note, note.is_todo ? 'note' : 'todo');
} }
static toggleTodoCompleted(note: NoteEntity) { public static toggleTodoCompleted(note: NoteEntity) {
if (!('todo_completed' in note)) throw new Error('Missing "todo_completed" property'); if (!('todo_completed' in note)) throw new Error('Missing "todo_completed" property');
note = Object.assign({}, note); note = Object.assign({}, note);
@ -589,7 +589,7 @@ export default class Note extends BaseItem {
return note; return note;
} }
static async duplicateMultipleNotes(noteIds: string[], options: any = null) { public static async duplicateMultipleNotes(noteIds: string[], options: any = null) {
// if options.uniqueTitle is true, a unique title for the duplicated file will be assigned. // if options.uniqueTitle is true, a unique title for the duplicated file will be assigned.
const ensureUniqueTitle = options && options.ensureUniqueTitle; const ensureUniqueTitle = options && options.ensureUniqueTitle;
@ -655,7 +655,7 @@ export default class Note extends BaseItem {
return this.save(newNoteSaved); return this.save(newNoteSaved);
} }
static async noteIsOlderThan(noteId: string, date: number) { public static async noteIsOlderThan(noteId: string, date: number) {
const n = await this.db().selectOne('SELECT updated_time FROM notes WHERE id = ?', [noteId]); const n = await this.db().selectOne('SELECT updated_time FROM notes WHERE id = ?', [noteId]);
if (!n) throw new Error(`No such note: ${noteId}`); if (!n) throw new Error(`No such note: ${noteId}`);
return n.updated_time < date; return n.updated_time < date;
@ -737,7 +737,7 @@ export default class Note extends BaseItem {
return note; return note;
} }
static async batchDelete(ids: string[], options: any = null) { public static async batchDelete(ids: string[], options: any = null) {
ids = ids.slice(); ids = ids.slice();
while (ids.length) { while (ids.length) {
@ -763,7 +763,7 @@ export default class Note extends BaseItem {
} }
} }
static async deleteMessage(noteIds: string[]): Promise<string|null> { public static async deleteMessage(noteIds: string[]): Promise<string|null> {
let msg = ''; let msg = '';
if (noteIds.length === 1) { if (noteIds.length === 1) {
const note = await Note.load(noteIds[0]); const note = await Note.load(noteIds[0]);
@ -775,15 +775,15 @@ export default class Note extends BaseItem {
return msg; return msg;
} }
static dueNotes() { public static dueNotes() {
return this.modelSelectAll('SELECT id, title, body, is_todo, todo_due, todo_completed, is_conflict FROM notes WHERE is_conflict = 0 AND is_todo = 1 AND todo_completed = 0 AND todo_due > ?', [time.unixMs()]); return this.modelSelectAll('SELECT id, title, body, is_todo, todo_due, todo_completed, is_conflict FROM notes WHERE is_conflict = 0 AND is_todo = 1 AND todo_completed = 0 AND todo_due > ?', [time.unixMs()]);
} }
static needAlarm(note: NoteEntity) { public static needAlarm(note: NoteEntity) {
return note.is_todo && !note.todo_completed && note.todo_due >= time.unixMs() && !note.is_conflict; return note.is_todo && !note.todo_completed && note.todo_due >= time.unixMs() && !note.is_conflict;
} }
static dueDateObject(note: NoteEntity) { public static dueDateObject(note: NoteEntity) {
if (!!note.is_todo && note.todo_due) { if (!!note.is_todo && note.todo_due) {
if (!this.dueDateObjects_) this.dueDateObjects_ = {}; if (!this.dueDateObjects_) this.dueDateObjects_ = {};
if (this.dueDateObjects_[note.todo_due]) return this.dueDateObjects_[note.todo_due]; if (this.dueDateObjects_[note.todo_due]) return this.dueDateObjects_[note.todo_due];
@ -795,7 +795,7 @@ export default class Note extends BaseItem {
} }
// Tells whether the conflict between the local and remote note can be ignored. // Tells whether the conflict between the local and remote note can be ignored.
static mustHandleConflict(localNote: NoteEntity, remoteNote: NoteEntity) { public static mustHandleConflict(localNote: NoteEntity, remoteNote: NoteEntity) {
// That shouldn't happen so throw an exception // That shouldn't happen so throw an exception
if (localNote.id !== remoteNote.id) throw new Error('Cannot handle conflict for two different notes'); if (localNote.id !== remoteNote.id) throw new Error('Cannot handle conflict for two different notes');
@ -809,7 +809,7 @@ export default class Note extends BaseItem {
return false; return false;
} }
static markupLanguageToLabel(markupLanguageId: number) { public static markupLanguageToLabel(markupLanguageId: number) {
if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) return 'Markdown'; if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) return 'Markdown';
if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_HTML) return 'HTML'; if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_HTML) return 'HTML';
throw new Error(`Invalid markup language ID: ${markupLanguageId}`); throw new Error(`Invalid markup language ID: ${markupLanguageId}`);
@ -818,13 +818,13 @@ export default class Note extends BaseItem {
// When notes are sorted in "custom order", they are sorted by the "order" field first and, // When notes are sorted in "custom order", they are sorted by the "order" field first and,
// in those cases, where the order field is the same for some notes, by created time. // in those cases, where the order field is the same for some notes, by created time.
// Further sorting by todo completion status, if enabled, is handled separately. // Further sorting by todo completion status, if enabled, is handled separately.
static customOrderByColumns() { public static customOrderByColumns() {
return [{ by: 'order', dir: 'DESC' }, { by: 'user_created_time', dir: 'DESC' }]; return [{ by: 'order', dir: 'DESC' }, { by: 'user_created_time', dir: 'DESC' }];
} }
// Update the note "order" field without changing the user timestamps, // Update the note "order" field without changing the user timestamps,
// which is generally what we want. // which is generally what we want.
static async updateNoteOrder_(note: NoteEntity, order: any) { private static async updateNoteOrder_(note: NoteEntity, order: any) {
return Note.save(Object.assign({}, note, { return Note.save(Object.assign({}, note, {
order: order, order: order,
user_updated_time: note.user_updated_time, user_updated_time: note.user_updated_time,
@ -836,7 +836,7 @@ export default class Note extends BaseItem {
// of unecessary updates, so it's the caller's responsability to update // of unecessary updates, so it's the caller's responsability to update
// the UI once the call is finished. This is done by listening to the // the UI once the call is finished. This is done by listening to the
// NOTE_IS_INSERTING_NOTES action in the application middleware. // NOTE_IS_INSERTING_NOTES action in the application middleware.
static async insertNotesAt(folderId: string, noteIds: string[], index: number, uncompletedTodosOnTop: boolean, showCompletedTodos: boolean) { public static async insertNotesAt(folderId: string, noteIds: string[], index: number, uncompletedTodosOnTop: boolean, showCompletedTodos: boolean) {
if (!noteIds.length) return; if (!noteIds.length) return;
const defer = () => { const defer = () => {
@ -985,19 +985,19 @@ export default class Note extends BaseItem {
} }
} }
static handleTitleNaturalSorting(items: NoteEntity[], options: any) { public static handleTitleNaturalSorting(items: NoteEntity[], options: any) {
if (options.order.length > 0 && options.order[0].by === 'title') { if (options.order.length > 0 && options.order[0].by === 'title') {
const collator = this.getNaturalSortingCollator(); const collator = this.getNaturalSortingCollator();
items.sort((a, b) => ((options.order[0].dir === 'ASC') ? 1 : -1) * collator.compare(a.title, b.title)); items.sort((a, b) => ((options.order[0].dir === 'ASC') ? 1 : -1) * collator.compare(a.title, b.title));
} }
} }
static getNaturalSortingCollator() { public static getNaturalSortingCollator() {
return new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); return new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
} }
static async createConflictNote(sourceNote: NoteEntity, changeSource: number): Promise<NoteEntity> { public static async createConflictNote(sourceNote: NoteEntity, changeSource: number): Promise<NoteEntity> {
const conflictNote = Object.assign({}, sourceNote); const conflictNote = Object.assign({}, sourceNote);
delete conflictNote.id; delete conflictNote.id;
conflictNote.is_conflict = 1; conflictNote.is_conflict = 1;

View File

@ -8,11 +8,11 @@ import BaseItem from './BaseItem';
// - If last_seen_time is 0, it means the resource has never been associated with any note. // - If last_seen_time is 0, it means the resource has never been associated with any note.
export default class NoteResource extends BaseModel { export default class NoteResource extends BaseModel {
static tableName() { public static tableName() {
return 'note_resources'; return 'note_resources';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_NOTE_RESOURCE; return BaseModel.TYPE_NOTE_RESOURCE;
} }
@ -71,12 +71,12 @@ export default class NoteResource extends BaseModel {
// await this.db().transactionExecBatch(queries); // await this.db().transactionExecBatch(queries);
// } // }
static async associatedNoteIds(resourceId: string): Promise<string[]> { public static async associatedNoteIds(resourceId: string): Promise<string[]> {
const rows = await this.modelSelectAll('SELECT note_id FROM note_resources WHERE resource_id = ? AND is_associated = 1', [resourceId]); const rows = await this.modelSelectAll('SELECT note_id FROM note_resources WHERE resource_id = ? AND is_associated = 1', [resourceId]);
return rows.map((r: any) => r.note_id); return rows.map((r: any) => r.note_id);
} }
static async setAssociatedResources(noteId: string, resourceIds: string[]) { public static async setAssociatedResources(noteId: string, resourceIds: string[]) {
const existingRows = await this.modelSelectAll('SELECT * FROM note_resources WHERE note_id = ?', [noteId]); const existingRows = await this.modelSelectAll('SELECT * FROM note_resources WHERE note_id = ?', [noteId]);
const notProcessedResourceIds = resourceIds.slice(); const notProcessedResourceIds = resourceIds.slice();
@ -100,7 +100,7 @@ export default class NoteResource extends BaseModel {
await this.db().transactionExecBatch(queries); await this.db().transactionExecBatch(queries);
} }
static async addOrphanedResources() { public static async addOrphanedResources() {
const missingResources = await this.db().selectAll('SELECT id FROM resources WHERE id NOT IN (SELECT DISTINCT resource_id FROM note_resources)'); const missingResources = await this.db().selectAll('SELECT id FROM resources WHERE id NOT IN (SELECT DISTINCT resource_id FROM note_resources)');
const queries = []; const queries = [];
for (let i = 0; i < missingResources.length; i++) { for (let i = 0; i < missingResources.length; i++) {
@ -125,11 +125,11 @@ export default class NoteResource extends BaseModel {
await this.db().transactionExecBatch(queries); await this.db().transactionExecBatch(queries);
} }
static async remove(noteId: string) { public static async remove(noteId: string) {
await this.db().exec({ sql: 'UPDATE note_resources SET is_associated = 0 WHERE note_id = ?', params: [noteId] }); await this.db().exec({ sql: 'UPDATE note_resources SET is_associated = 0 WHERE note_id = ?', params: [noteId] });
} }
static async orphanResources(expiryDelay: number = null) { public static async orphanResources(expiryDelay: number = null) {
if (expiryDelay === null) expiryDelay = 1000 * 60 * 60 * 24 * 10; if (expiryDelay === null) expiryDelay = 1000 * 60 * 60 * 24 * 10;
const cutOffTime = Date.now() - expiryDelay; const cutOffTime = Date.now() - expiryDelay;
const output = await this.modelSelectAll( const output = await this.modelSelectAll(
@ -146,7 +146,7 @@ export default class NoteResource extends BaseModel {
return output.map((r: any) => r.resource_id); return output.map((r: any) => r.resource_id);
} }
static async deleteByResource(resourceId: string) { public static async deleteByResource(resourceId: string) {
await this.db().exec('DELETE FROM note_resources WHERE resource_id = ?', [resourceId]); await this.db().exec('DELETE FROM note_resources WHERE resource_id = ?', [resourceId]);
} }
} }

View File

@ -2,20 +2,20 @@ import BaseItem from './BaseItem';
import BaseModel from '../BaseModel'; import BaseModel from '../BaseModel';
export default class NoteTag extends BaseItem { export default class NoteTag extends BaseItem {
static tableName() { public static tableName() {
return 'note_tags'; return 'note_tags';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_NOTE_TAG; return BaseModel.TYPE_NOTE_TAG;
} }
static async byNoteIds(noteIds: string[]) { public static async byNoteIds(noteIds: string[]) {
if (!noteIds.length) return []; if (!noteIds.length) return [];
return this.modelSelectAll(`SELECT * FROM note_tags WHERE note_id IN ("${noteIds.join('","')}")`); return this.modelSelectAll(`SELECT * FROM note_tags WHERE note_id IN ("${noteIds.join('","')}")`);
} }
static async tagIdsByNoteId(noteId: string) { public static async tagIdsByNoteId(noteId: string) {
const rows = await this.db().selectAll('SELECT tag_id FROM note_tags WHERE note_id = ?', [noteId]); const rows = await this.db().selectAll('SELECT tag_id FROM note_tags WHERE note_id = ?', [noteId]);
const output = []; const output = [];
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {

View File

@ -29,15 +29,15 @@ export default class Resource extends BaseItem {
public static fsDriver_: any; public static fsDriver_: any;
static tableName() { public static tableName() {
return 'resources'; return 'resources';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_RESOURCE; return BaseModel.TYPE_RESOURCE;
} }
static encryptionService() { public static encryptionService() {
if (!this.encryptionService_) throw new Error('Resource.encryptionService_ is not set!!'); if (!this.encryptionService_) throw new Error('Resource.encryptionService_ is not set!!');
return this.encryptionService_; return this.encryptionService_;
} }
@ -47,12 +47,12 @@ export default class Resource extends BaseItem {
return this.shareService_; return this.shareService_;
} }
static isSupportedImageMimeType(type: string) { public static isSupportedImageMimeType(type: string) {
const imageMimeTypes = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp']; const imageMimeTypes = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp'];
return imageMimeTypes.indexOf(type.toLowerCase()) >= 0; return imageMimeTypes.indexOf(type.toLowerCase()) >= 0;
} }
static fetchStatuses(resourceIds: string[]): Promise<any[]> { public static fetchStatuses(resourceIds: string[]): Promise<any[]> {
if (!resourceIds.length) return Promise.resolve([]); if (!resourceIds.length) return Promise.resolve([]);
return this.db().selectAll(`SELECT resource_id, fetch_status FROM resource_local_states WHERE resource_id IN ("${resourceIds.join('","')}")`); return this.db().selectAll(`SELECT resource_id, fetch_status FROM resource_local_states WHERE resource_id IN ("${resourceIds.join('","')}")`);
} }
@ -61,7 +61,7 @@ export default class Resource extends BaseItem {
return this.db().selectAllFields('SELECT id FROM resources WHERE is_shared = 1', [], 'id'); return this.db().selectAllFields('SELECT id FROM resources WHERE is_shared = 1', [], 'id');
} }
static errorFetchStatuses() { public static errorFetchStatuses() {
return this.db().selectAll(` return this.db().selectAll(`
SELECT title AS resource_title, resource_id, fetch_error SELECT title AS resource_title, resource_id, fetch_error
FROM resource_local_states FROM resource_local_states
@ -70,7 +70,7 @@ export default class Resource extends BaseItem {
`, [Resource.FETCH_STATUS_ERROR]); `, [Resource.FETCH_STATUS_ERROR]);
} }
static needToBeFetched(resourceDownloadMode: string = null, limit: number = null) { public static needToBeFetched(resourceDownloadMode: string = null, limit: number = null) {
const sql = ['SELECT * FROM resources WHERE encryption_applied = 0 AND id IN (SELECT resource_id FROM resource_local_states WHERE fetch_status = ?)']; const sql = ['SELECT * FROM resources WHERE encryption_applied = 0 AND id IN (SELECT resource_id FROM resource_local_states WHERE fetch_status = ?)'];
if (resourceDownloadMode !== 'always') { if (resourceDownloadMode !== 'always') {
sql.push('AND resources.id IN (SELECT resource_id FROM resources_to_download)'); sql.push('AND resources.id IN (SELECT resource_id FROM resources_to_download)');
@ -80,21 +80,21 @@ export default class Resource extends BaseItem {
return this.modelSelectAll(sql.join(' '), [Resource.FETCH_STATUS_IDLE]); return this.modelSelectAll(sql.join(' '), [Resource.FETCH_STATUS_IDLE]);
} }
static async resetStartedFetchStatus() { public static async resetStartedFetchStatus() {
return await this.db().exec('UPDATE resource_local_states SET fetch_status = ? WHERE fetch_status = ?', [Resource.FETCH_STATUS_IDLE, Resource.FETCH_STATUS_STARTED]); return await this.db().exec('UPDATE resource_local_states SET fetch_status = ? WHERE fetch_status = ?', [Resource.FETCH_STATUS_IDLE, Resource.FETCH_STATUS_STARTED]);
} }
static resetErrorStatus(resourceId: string) { public static resetErrorStatus(resourceId: string) {
return this.db().exec('UPDATE resource_local_states SET fetch_status = ?, fetch_error = "" WHERE resource_id = ?', [Resource.FETCH_STATUS_IDLE, resourceId]); return this.db().exec('UPDATE resource_local_states SET fetch_status = ?, fetch_error = "" WHERE resource_id = ?', [Resource.FETCH_STATUS_IDLE, resourceId]);
} }
static fsDriver() { public static fsDriver() {
if (!Resource.fsDriver_) Resource.fsDriver_ = new FsDriverDummy(); if (!Resource.fsDriver_) Resource.fsDriver_ = new FsDriverDummy();
return Resource.fsDriver_; return Resource.fsDriver_;
} }
// DEPRECATED IN FAVOUR OF friendlySafeFilename() // DEPRECATED IN FAVOUR OF friendlySafeFilename()
static friendlyFilename(resource: ResourceEntity) { public static friendlyFilename(resource: ResourceEntity) {
let output = safeFilename(resource.title); // Make sure not to allow spaces or any special characters as it's not supported in HTTP headers let output = safeFilename(resource.title); // Make sure not to allow spaces or any special characters as it's not supported in HTTP headers
if (!output) output = resource.id; if (!output) output = resource.id;
let extension = resource.file_extension; let extension = resource.file_extension;
@ -103,22 +103,22 @@ export default class Resource extends BaseItem {
return output + extension; return output + extension;
} }
static baseDirectoryPath() { public static baseDirectoryPath() {
return Setting.value('resourceDir'); return Setting.value('resourceDir');
} }
static baseRelativeDirectoryPath() { public static baseRelativeDirectoryPath() {
return Setting.value('resourceDirName'); return Setting.value('resourceDirName');
} }
static filename(resource: ResourceEntity, encryptedBlob = false) { public static filename(resource: ResourceEntity, encryptedBlob = false) {
let extension = encryptedBlob ? 'crypted' : resource.file_extension; let extension = encryptedBlob ? 'crypted' : resource.file_extension;
if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : ''; if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : '';
extension = extension ? `.${extension}` : ''; extension = extension ? `.${extension}` : '';
return resource.id + extension; return resource.id + extension;
} }
static friendlySafeFilename(resource: ResourceEntity) { public static friendlySafeFilename(resource: ResourceEntity) {
let ext = resource.file_extension; let ext = resource.file_extension;
if (!ext) ext = resource.mime ? mime.toFileExtension(resource.mime) : ''; if (!ext) ext = resource.mime ? mime.toFileExtension(resource.mime) : '';
const safeExt = ext ? pathUtils.safeFileExtension(ext).toLowerCase() : ''; const safeExt = ext ? pathUtils.safeFileExtension(ext).toLowerCase() : '';
@ -127,20 +127,20 @@ export default class Resource extends BaseItem {
return pathUtils.friendlySafeFilename(title) + (safeExt ? `.${safeExt}` : ''); return pathUtils.friendlySafeFilename(title) + (safeExt ? `.${safeExt}` : '');
} }
static relativePath(resource: ResourceEntity, encryptedBlob = false) { public static relativePath(resource: ResourceEntity, encryptedBlob = false) {
return `${Setting.value('resourceDirName')}/${this.filename(resource, encryptedBlob)}`; return `${Setting.value('resourceDirName')}/${this.filename(resource, encryptedBlob)}`;
} }
static fullPath(resource: ResourceEntity, encryptedBlob = false) { public static fullPath(resource: ResourceEntity, encryptedBlob = false) {
return `${Setting.value('resourceDir')}/${this.filename(resource, encryptedBlob)}`; return `${Setting.value('resourceDir')}/${this.filename(resource, encryptedBlob)}`;
} }
static async isReady(resource: ResourceEntity) { public static async isReady(resource: ResourceEntity) {
const r = await this.readyStatus(resource); const r = await this.readyStatus(resource);
return r === 'ok'; return r === 'ok';
} }
static async readyStatus(resource: ResourceEntity) { public static async readyStatus(resource: ResourceEntity) {
const ls = await this.localState(resource); const ls = await this.localState(resource);
if (!resource) return 'notFound'; if (!resource) return 'notFound';
if (ls.fetch_status !== Resource.FETCH_STATUS_DONE) return 'notDownloaded'; if (ls.fetch_status !== Resource.FETCH_STATUS_DONE) return 'notDownloaded';
@ -148,13 +148,13 @@ export default class Resource extends BaseItem {
return 'ok'; return 'ok';
} }
static async requireIsReady(resource: ResourceEntity) { public static async requireIsReady(resource: ResourceEntity) {
const readyStatus = await Resource.readyStatus(resource); const readyStatus = await Resource.readyStatus(resource);
if (readyStatus !== 'ok') throw new Error(`Resource is not ready. Status: ${readyStatus}`); if (readyStatus !== 'ok') throw new Error(`Resource is not ready. Status: ${readyStatus}`);
} }
// For resources, we need to decrypt the item (metadata) and the resource binary blob. // For resources, we need to decrypt the item (metadata) and the resource binary blob.
static async decrypt(item: ResourceEntity) { public static async decrypt(item: ResourceEntity) {
// The item might already be decrypted but not the blob (for instance if it crashes while // The item might already be decrypted but not the blob (for instance if it crashes while
// decrypting the blob or was otherwise interrupted). // decrypting the blob or was otherwise interrupted).
const decryptedItem = item.encryption_cipher_text ? await super.decrypt(item) : Object.assign({}, item); const decryptedItem = item.encryption_cipher_text ? await super.decrypt(item) : Object.assign({}, item);
@ -230,7 +230,7 @@ export default class Resource extends BaseItem {
return { path: encryptedPath, resource: resourceCopy }; return { path: encryptedPath, resource: resourceCopy };
} }
static markdownTag(resource: any) { public static markdownTag(resource: any) {
let tagAlt = resource.alt ? resource.alt : resource.title; let tagAlt = resource.alt ? resource.alt : resource.title;
if (!tagAlt) tagAlt = ''; if (!tagAlt) tagAlt = '';
const lines = []; const lines = [];
@ -246,48 +246,48 @@ export default class Resource extends BaseItem {
return lines.join(''); return lines.join('');
} }
static internalUrl(resource: ResourceEntity) { public static internalUrl(resource: ResourceEntity) {
return `:/${resource.id}`; return `:/${resource.id}`;
} }
static pathToId(path: string) { public static pathToId(path: string) {
return filename(path); return filename(path);
} }
static async content(resource: ResourceEntity) { public static async content(resource: ResourceEntity) {
return this.fsDriver().readFile(this.fullPath(resource), 'Buffer'); return this.fsDriver().readFile(this.fullPath(resource), 'Buffer');
} }
static isResourceUrl(url: string) { public static isResourceUrl(url: string) {
return url && url.length === 34 && url[0] === ':' && url[1] === '/'; return url && url.length === 34 && url[0] === ':' && url[1] === '/';
} }
static urlToId(url: string) { public static urlToId(url: string) {
if (!this.isResourceUrl(url)) throw new Error(`Not a valid resource URL: ${url}`); if (!this.isResourceUrl(url)) throw new Error(`Not a valid resource URL: ${url}`);
return url.substr(2); return url.substr(2);
} }
static async localState(resourceOrId: any) { public static async localState(resourceOrId: any) {
return ResourceLocalState.byResourceId(typeof resourceOrId === 'object' ? resourceOrId.id : resourceOrId); return ResourceLocalState.byResourceId(typeof resourceOrId === 'object' ? resourceOrId.id : resourceOrId);
} }
static async setLocalState(resourceOrId: any, state: ResourceLocalStateEntity) { public static async setLocalState(resourceOrId: any, state: ResourceLocalStateEntity) {
const id = typeof resourceOrId === 'object' ? resourceOrId.id : resourceOrId; const id = typeof resourceOrId === 'object' ? resourceOrId.id : resourceOrId;
await ResourceLocalState.save(Object.assign({}, state, { resource_id: id })); await ResourceLocalState.save(Object.assign({}, state, { resource_id: id }));
} }
static async needFileSizeSet() { public static async needFileSizeSet() {
return this.modelSelectAll('SELECT * FROM resources WHERE `size` < 0 AND encryption_blob_encrypted = 0'); return this.modelSelectAll('SELECT * FROM resources WHERE `size` < 0 AND encryption_blob_encrypted = 0');
} }
// Only set the `size` field and nothing else, not even the update_time // Only set the `size` field and nothing else, not even the update_time
// This is because it's only necessary to do it once after migration 20 // This is because it's only necessary to do it once after migration 20
// and each client does it so there's no need to sync the resource. // and each client does it so there's no need to sync the resource.
static async setFileSizeOnly(resourceId: string, fileSize: number) { public static async setFileSizeOnly(resourceId: string, fileSize: number) {
return this.db().exec('UPDATE resources set `size` = ? WHERE id = ?', [fileSize, resourceId]); return this.db().exec('UPDATE resources set `size` = ? WHERE id = ?', [fileSize, resourceId]);
} }
static async batchDelete(ids: string[], options: any = null) { public static async batchDelete(ids: string[], options: any = null) {
// For resources, there's not really batch deleting since there's the file data to delete // For resources, there's not really batch deleting since there's the file data to delete
// too, so each is processed one by one with the item being deleted last (since the db // too, so each is processed one by one with the item being deleted last (since the db
// call is the less likely to fail). // call is the less likely to fail).
@ -305,13 +305,13 @@ export default class Resource extends BaseItem {
await ResourceLocalState.batchDelete(ids); await ResourceLocalState.batchDelete(ids);
} }
static async markForDownload(resourceId: string) { public static async markForDownload(resourceId: string) {
// Insert the row only if it's not already there // Insert the row only if it's not already there
const t = Date.now(); const t = Date.now();
await this.db().exec('INSERT INTO resources_to_download (resource_id, updated_time, created_time) SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM resources_to_download WHERE resource_id = ?)', [resourceId, t, t, resourceId]); await this.db().exec('INSERT INTO resources_to_download (resource_id, updated_time, created_time) SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM resources_to_download WHERE resource_id = ?)', [resourceId, t, t, resourceId]);
} }
static async downloadedButEncryptedBlobCount(excludedIds: string[] = null) { public static async downloadedButEncryptedBlobCount(excludedIds: string[] = null) {
let excludedSql = ''; let excludedSql = '';
if (excludedIds && excludedIds.length) { if (excludedIds && excludedIds.length) {
excludedSql = `AND resource_id NOT IN ("${excludedIds.join('","')}")`; excludedSql = `AND resource_id NOT IN ("${excludedIds.join('","')}")`;
@ -328,7 +328,7 @@ export default class Resource extends BaseItem {
return r ? r.total : 0; return r ? r.total : 0;
} }
static async downloadStatusCounts(status: number) { public static async downloadStatusCounts(status: number) {
const r = await this.db().selectOne(` const r = await this.db().selectOne(`
SELECT count(*) as total SELECT count(*) as total
FROM resource_local_states FROM resource_local_states
@ -338,7 +338,7 @@ export default class Resource extends BaseItem {
return r ? r.total : 0; return r ? r.total : 0;
} }
static async createdLocallyCount() { public static async createdLocallyCount() {
const r = await this.db().selectOne(` const r = await this.db().selectOne(`
SELECT count(*) as total SELECT count(*) as total
FROM resources FROM resources
@ -349,7 +349,7 @@ export default class Resource extends BaseItem {
return r ? r.total : 0; return r ? r.total : 0;
} }
static fetchStatusToLabel(status: number) { public static fetchStatusToLabel(status: number) {
if (status === Resource.FETCH_STATUS_IDLE) return _('Not downloaded'); if (status === Resource.FETCH_STATUS_IDLE) return _('Not downloaded');
if (status === Resource.FETCH_STATUS_STARTED) return _('Downloading'); if (status === Resource.FETCH_STATUS_STARTED) return _('Downloading');
if (status === Resource.FETCH_STATUS_DONE) return _('Downloaded'); if (status === Resource.FETCH_STATUS_DONE) return _('Downloaded');
@ -357,7 +357,7 @@ export default class Resource extends BaseItem {
throw new Error(`Invalid status: ${status}`); throw new Error(`Invalid status: ${status}`);
} }
static async updateResourceBlobContent(resourceId: string, newBlobFilePath: string) { public static async updateResourceBlobContent(resourceId: string, newBlobFilePath: string) {
const resource = await Resource.load(resourceId); const resource = await Resource.load(resourceId);
await this.requireIsReady(resource); await this.requireIsReady(resource);
@ -370,7 +370,7 @@ export default class Resource extends BaseItem {
}); });
} }
static async resourceBlobContent(resourceId: string, encoding = 'Buffer') { public static async resourceBlobContent(resourceId: string, encoding = 'Buffer') {
const resource = await Resource.load(resourceId); const resource = await Resource.load(resourceId);
await this.requireIsReady(resource); await this.requireIsReady(resource);
return await this.fsDriver().readFile(Resource.fullPath(resource), encoding); return await this.fsDriver().readFile(Resource.fullPath(resource), encoding);

View File

@ -3,15 +3,15 @@ import { ResourceLocalStateEntity } from '../services/database/types';
import Database from '../database'; import Database from '../database';
export default class ResourceLocalState extends BaseModel { export default class ResourceLocalState extends BaseModel {
static tableName() { public static tableName() {
return 'resource_local_states'; return 'resource_local_states';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_RESOURCE_LOCAL_STATE; return BaseModel.TYPE_RESOURCE_LOCAL_STATE;
} }
static async byResourceId(resourceId: string) { public static async byResourceId(resourceId: string) {
if (!resourceId) throw new Error('Resource ID not provided'); // Sanity check if (!resourceId) throw new Error('Resource ID not provided'); // Sanity check
const result = await this.modelSelectOne('SELECT * FROM resource_local_states WHERE resource_id = ?', [resourceId]); const result = await this.modelSelectOne('SELECT * FROM resource_local_states WHERE resource_id = ?', [resourceId]);
@ -26,13 +26,13 @@ export default class ResourceLocalState extends BaseModel {
return result; return result;
} }
static async save(o: ResourceLocalStateEntity) { public static async save(o: ResourceLocalStateEntity) {
const queries = [{ sql: 'DELETE FROM resource_local_states WHERE resource_id = ?', params: [o.resource_id] }, Database.insertQuery(this.tableName(), o)]; const queries = [{ sql: 'DELETE FROM resource_local_states WHERE resource_id = ?', params: [o.resource_id] }, Database.insertQuery(this.tableName(), o)];
return this.db().transactionExecBatch(queries); return this.db().transactionExecBatch(queries);
} }
static batchDelete(ids: string[], options: any = null) { public static batchDelete(ids: string[], options: any = null) {
options = options ? Object.assign({}, options) : {}; options = options ? Object.assign({}, options) : {};
options.idFieldName = 'resource_id'; options.idFieldName = 'resource_id';
return super.batchDelete(ids, options); return super.batchDelete(ids, options);

View File

@ -14,11 +14,11 @@ export interface ObjectPatch {
} }
export default class Revision extends BaseItem { export default class Revision extends BaseItem {
static tableName() { public static tableName() {
return 'revisions'; return 'revisions';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_REVISION; return BaseModel.TYPE_REVISION;
} }
@ -181,7 +181,7 @@ export default class Revision extends BaseItem {
}; };
} }
static revisionPatchStatsText(rev: RevisionEntity) { public static revisionPatchStatsText(rev: RevisionEntity) {
const titleStats = this.patchStats(rev.title_diff); const titleStats = this.patchStats(rev.title_diff);
const bodyStats = this.patchStats(rev.body_diff); const bodyStats = this.patchStats(rev.body_diff);
const total = { const total = {
@ -195,28 +195,28 @@ export default class Revision extends BaseItem {
return output.join(', '); return output.join(', ');
} }
static async countRevisions(itemType: ModelType, itemId: string) { public static async countRevisions(itemType: ModelType, itemId: string) {
const r = await this.db().selectOne('SELECT count(*) as total FROM revisions WHERE item_type = ? AND item_id = ?', [itemType, itemId]); const r = await this.db().selectOne('SELECT count(*) as total FROM revisions WHERE item_type = ? AND item_id = ?', [itemType, itemId]);
return r ? r.total : 0; return r ? r.total : 0;
} }
static latestRevision(itemType: ModelType, itemId: string) { public static latestRevision(itemType: ModelType, itemId: string) {
return this.modelSelectOne('SELECT * FROM revisions WHERE item_type = ? AND item_id = ? ORDER BY item_updated_time DESC LIMIT 1', [itemType, itemId]); return this.modelSelectOne('SELECT * FROM revisions WHERE item_type = ? AND item_id = ? ORDER BY item_updated_time DESC LIMIT 1', [itemType, itemId]);
} }
static allByType(itemType: ModelType, itemId: string) { public static allByType(itemType: ModelType, itemId: string) {
return this.modelSelectAll('SELECT * FROM revisions WHERE item_type = ? AND item_id = ? ORDER BY item_updated_time ASC', [itemType, itemId]); return this.modelSelectAll('SELECT * FROM revisions WHERE item_type = ? AND item_id = ? ORDER BY item_updated_time ASC', [itemType, itemId]);
} }
static async itemsWithRevisions(itemType: ModelType, itemIds: string[]) { public static async itemsWithRevisions(itemType: ModelType, itemIds: string[]) {
if (!itemIds.length) return []; if (!itemIds.length) return [];
const rows = await this.db().selectAll(`SELECT distinct item_id FROM revisions WHERE item_type = ? AND item_id IN ("${itemIds.join('","')}")`, [itemType]); const rows = await this.db().selectAll(`SELECT distinct item_id FROM revisions WHERE item_type = ? AND item_id IN ("${itemIds.join('","')}")`, [itemType]);
return rows.map((r: RevisionEntity) => r.item_id); return rows.map((r: RevisionEntity) => r.item_id);
} }
static async itemsWithNoRevisions(itemType: ModelType, itemIds: string[]) { public static async itemsWithNoRevisions(itemType: ModelType, itemIds: string[]) {
const withRevs = await this.itemsWithRevisions(itemType, itemIds); const withRevs = await this.itemsWithRevisions(itemType, itemIds);
const output = []; const output = [];
for (let i = 0; i < itemIds.length; i++) { for (let i = 0; i < itemIds.length; i++) {
@ -225,7 +225,7 @@ export default class Revision extends BaseItem {
return ArrayUtils.unique(output); return ArrayUtils.unique(output);
} }
static moveRevisionToTop(revision: RevisionEntity, revs: RevisionEntity[]) { public static moveRevisionToTop(revision: RevisionEntity, revs: RevisionEntity[]) {
let targetIndex = -1; let targetIndex = -1;
for (let i = revs.length - 1; i >= 0; i--) { for (let i = revs.length - 1; i >= 0; i--) {
const rev = revs[i]; const rev = revs[i];
@ -295,7 +295,7 @@ export default class Revision extends BaseItem {
return output; return output;
} }
static async deleteOldRevisions(ttl: number) { public static async deleteOldRevisions(ttl: number) {
// When deleting old revisions, we need to make sure that the oldest surviving revision // When deleting old revisions, we need to make sure that the oldest surviving revision
// is a "merged" one (as opposed to a diff from a now deleted revision). So every time // is a "merged" one (as opposed to a diff from a now deleted revision). So every time
// we deleted a revision, we need to find if there's a corresponding surviving revision // we deleted a revision, we need to find if there's a corresponding surviving revision
@ -342,7 +342,7 @@ export default class Revision extends BaseItem {
} }
} }
static async revisionExists(itemType: ModelType, itemId: string, updatedTime: number) { public static async revisionExists(itemType: ModelType, itemId: string, updatedTime: number) {
const existingRev = await Revision.latestRevision(itemType, itemId); const existingRev = await Revision.latestRevision(itemType, itemId);
return existingRev && existingRev.item_updated_time === updatedTime; return existingRev && existingRev.item_updated_time === updatedTime;
} }

View File

@ -3,15 +3,15 @@
import BaseModel from '../BaseModel'; import BaseModel from '../BaseModel';
export default class Search extends BaseModel { export default class Search extends BaseModel {
static tableName(): string { public static tableName(): string {
throw new Error('Not using database'); throw new Error('Not using database');
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_SEARCH; return BaseModel.TYPE_SEARCH;
} }
static keywords(query: string) { public static keywords(query: string) {
let output: any = query.trim(); let output: any = query.trim();
output = output.split(/[\s\t\n]+/); output = output.split(/[\s\t\n]+/);
output = output.filter((o: any) => !!o); output = output.filter((o: any) => !!o);

View File

@ -313,11 +313,11 @@ class Setting extends BaseModel {
private static rootFileHandler_: FileHandler = null; private static rootFileHandler_: FileHandler = null;
private static settingFilename_: string = 'settings.json'; private static settingFilename_: string = 'settings.json';
static tableName() { public static tableName() {
return 'settings'; return 'settings';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_SETTING; return BaseModel.TYPE_SETTING;
} }
@ -365,16 +365,16 @@ class Setting extends BaseModel {
return this.rootFileHandler_; return this.rootFileHandler_;
} }
static keychainService() { public static keychainService() {
if (!this.keychainService_) throw new Error('keychainService has not been set!!'); if (!this.keychainService_) throw new Error('keychainService has not been set!!');
return this.keychainService_; return this.keychainService_;
} }
static setKeychainService(s: any) { public static setKeychainService(s: any) {
this.keychainService_ = s; this.keychainService_ = s;
} }
static metadata(): SettingItems { public static metadata(): SettingItems {
if (this.metadata_) return this.metadata_; if (this.metadata_) return this.metadata_;
const platform = shim.platformName(); const platform = shim.platformName();
@ -1750,7 +1750,7 @@ class Setting extends BaseModel {
if (type < 0) throw new Error(`Invalid setting type: ${type}`); if (type < 0) throw new Error(`Invalid setting type: ${type}`);
} }
static async registerSetting(key: string, metadataItem: SettingItem) { public static async registerSetting(key: string, metadataItem: SettingItem) {
try { try {
if (metadataItem.isEnum && !metadataItem.options) throw new Error('The `options` property is required for enum types'); if (metadataItem.isEnum && !metadataItem.options) throw new Error('The `options` property is required for enum types');
@ -1786,11 +1786,11 @@ class Setting extends BaseModel {
} }
} }
static async registerSection(name: string, source: SettingSectionSource, section: SettingSection) { public static async registerSection(name: string, source: SettingSectionSource, section: SettingSection) {
this.customSections_[name] = { ...section, name: name, source: source }; this.customSections_[name] = { ...section, name: name, source: source };
} }
static settingMetadata(key: string): SettingItem { public static settingMetadata(key: string): SettingItem {
const metadata = this.metadata(); const metadata = this.metadata();
if (!(key in metadata)) throw new Error(`Unknown key: ${key}`); if (!(key in metadata)) throw new Error(`Unknown key: ${key}`);
const output = Object.assign({}, metadata[key]); const output = Object.assign({}, metadata[key]);
@ -1812,17 +1812,17 @@ class Setting extends BaseModel {
return !!this.cache_.find(d => d.key === key); return !!this.cache_.find(d => d.key === key);
} }
static keyDescription(key: string, appType: AppType = null) { public static keyDescription(key: string, appType: AppType = null) {
const md = this.settingMetadata(key); const md = this.settingMetadata(key);
if (!md.description) return null; if (!md.description) return null;
return md.description(appType); return md.description(appType);
} }
static isSecureKey(key: string) { public static isSecureKey(key: string) {
return this.metadata()[key] && this.metadata()[key].secure === true; return this.metadata()[key] && this.metadata()[key].secure === true;
} }
static keys(publicOnly: boolean = false, appType: AppType = null, options: KeysOptions = null) { public static keys(publicOnly: boolean = false, appType: AppType = null, options: KeysOptions = null) {
options = Object.assign({}, { options = Object.assign({}, {
secureOnly: false, secureOnly: false,
}, options); }, options);
@ -1851,7 +1851,7 @@ class Setting extends BaseModel {
} }
} }
static isPublic(key: string) { public static isPublic(key: string) {
return this.keys(true).indexOf(key) >= 0; return this.keys(true).indexOf(key) >= 0;
} }
@ -1967,7 +1967,7 @@ class Setting extends BaseModel {
return md.storage || SettingStorage.Database; return md.storage || SettingStorage.Database;
} }
static toPlainObject() { public static toPlainObject() {
const keys = this.keys(); const keys = this.keys();
const keyToValues: any = {}; const keyToValues: any = {};
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
@ -1976,14 +1976,14 @@ class Setting extends BaseModel {
return keyToValues; return keyToValues;
} }
static dispatchUpdateAll() { public static dispatchUpdateAll() {
this.dispatch({ this.dispatch({
type: 'SETTING_UPDATE_ALL', type: 'SETTING_UPDATE_ALL',
settings: this.toPlainObject(), settings: this.toPlainObject(),
}); });
} }
static setConstant(key: string, value: any) { public static setConstant(key: string, value: any) {
if (!(key in this.constants_)) throw new Error(`Unknown constant key: ${key}`); if (!(key in this.constants_)) throw new Error(`Unknown constant key: ${key}`);
(this.constants_ as any)[key] = value; (this.constants_ as any)[key] = value;
} }
@ -2046,11 +2046,11 @@ class Setting extends BaseModel {
this.scheduleChangeEvent(); this.scheduleChangeEvent();
} }
static incValue(key: string, inc: any) { public static incValue(key: string, inc: any) {
return this.setValue(key, this.value(key) + inc); return this.setValue(key, this.value(key) + inc);
} }
static toggle(key: string) { public static toggle(key: string) {
return this.setValue(key, !this.value(key)); return this.setValue(key, !this.value(key));
} }
@ -2065,27 +2065,27 @@ class Setting extends BaseModel {
return false; return false;
} }
static objectValue(settingKey: string, objectKey: string, defaultValue: any = null) { public static objectValue(settingKey: string, objectKey: string, defaultValue: any = null) {
const o = this.value(settingKey); const o = this.value(settingKey);
if (!o || !(objectKey in o)) return defaultValue; if (!o || !(objectKey in o)) return defaultValue;
return o[objectKey]; return o[objectKey];
} }
static setObjectValue(settingKey: string, objectKey: string, value: any) { public static setObjectValue(settingKey: string, objectKey: string, value: any) {
let o = this.value(settingKey); let o = this.value(settingKey);
if (typeof o !== 'object') o = {}; if (typeof o !== 'object') o = {};
o[objectKey] = value; o[objectKey] = value;
this.setValue(settingKey, o); this.setValue(settingKey, o);
} }
static deleteObjectValue(settingKey: string, objectKey: string) { public static deleteObjectValue(settingKey: string, objectKey: string) {
const o = this.value(settingKey); const o = this.value(settingKey);
if (typeof o !== 'object') return; if (typeof o !== 'object') return;
delete o[objectKey]; delete o[objectKey];
this.setValue(settingKey, o); this.setValue(settingKey, o);
} }
static async deleteKeychainPasswords() { public static async deleteKeychainPasswords() {
const secureKeys = this.keys(false, null, { secureOnly: true }); const secureKeys = this.keys(false, null, { secureOnly: true });
for (const key of secureKeys) { for (const key of secureKeys) {
await this.keychainService().deletePassword(`setting.${key}`); await this.keychainService().deletePassword(`setting.${key}`);
@ -2121,7 +2121,7 @@ class Setting extends BaseModel {
return output; return output;
} }
static valueToString(key: string, value: any) { public static valueToString(key: string, value: any) {
const md = this.settingMetadata(key); const md = this.settingMetadata(key);
value = this.formatValue(key, value); value = this.formatValue(key, value);
if (md.type === SettingItemType.Int) return value.toFixed(0); if (md.type === SettingItemType.Int) return value.toFixed(0);
@ -2133,12 +2133,12 @@ class Setting extends BaseModel {
throw new Error(`Unhandled value type: ${md.type}`); throw new Error(`Unhandled value type: ${md.type}`);
} }
static filterValue(key: string, value: any) { public static filterValue(key: string, value: any) {
const md = this.settingMetadata(key); const md = this.settingMetadata(key);
return md.filter ? md.filter(value) : value; return md.filter ? md.filter(value) : value;
} }
static formatValue(key: string | SettingItemType, value: any) { public static formatValue(key: string | SettingItemType, value: any) {
const type = typeof key === 'string' ? this.settingMetadata(key).type : key; const type = typeof key === 'string' ? this.settingMetadata(key).type : key;
if (type === SettingItemType.Int) return !value ? 0 : Math.floor(Number(value)); if (type === SettingItemType.Int) return !value ? 0 : Math.floor(Number(value));
@ -2175,7 +2175,7 @@ class Setting extends BaseModel {
throw new Error(`Unhandled value type: ${type}`); throw new Error(`Unhandled value type: ${type}`);
} }
static value(key: string) { public static value(key: string) {
// Need to copy arrays and objects since in setValue(), the old value and new one is compared // Need to copy arrays and objects since in setValue(), the old value and new one is compared
// with strict equality and the value is updated only if changed. However if the caller acquire // with strict equality and the value is updated only if changed. However if the caller acquire
// and object and change a key, the objects will be detected as equal. By returning a copy // and object and change a key, the objects will be detected as equal. By returning a copy
@ -2212,12 +2212,12 @@ class Setting extends BaseModel {
return this.value(key); return this.value(key);
} }
static isEnum(key: string) { public static isEnum(key: string) {
const md = this.settingMetadata(key); const md = this.settingMetadata(key);
return md.isEnum === true; return md.isEnum === true;
} }
static enumOptionValues(key: string) { public static enumOptionValues(key: string) {
const options = this.enumOptions(key); const options = this.enumOptions(key);
const output = []; const output = [];
for (const n in options) { for (const n in options) {
@ -2227,7 +2227,7 @@ class Setting extends BaseModel {
return output; return output;
} }
static enumOptionLabel(key: string, value: any) { public static enumOptionLabel(key: string, value: any) {
const options = this.enumOptions(key); const options = this.enumOptions(key);
for (const n in options) { for (const n in options) {
if (n === value) return options[n]; if (n === value) return options[n];
@ -2235,14 +2235,14 @@ class Setting extends BaseModel {
return ''; return '';
} }
static enumOptions(key: string) { public static enumOptions(key: string) {
const metadata = this.metadata(); const metadata = this.metadata();
if (!metadata[key]) throw new Error(`Unknown key: ${key}`); if (!metadata[key]) throw new Error(`Unknown key: ${key}`);
if (!metadata[key].options) throw new Error(`No options for: ${key}`); if (!metadata[key].options) throw new Error(`No options for: ${key}`);
return metadata[key].options(); return metadata[key].options();
} }
static enumOptionsDoc(key: string, templateString: string = null) { public static enumOptionsDoc(key: string, templateString: string = null) {
if (templateString === null) templateString = '%s: %s'; if (templateString === null) templateString = '%s: %s';
const options = this.enumOptions(key); const options = this.enumOptions(key);
const output = []; const output = [];
@ -2253,7 +2253,7 @@ class Setting extends BaseModel {
return output.join(', '); return output.join(', ');
} }
static isAllowedEnumOption(key: string, value: any) { public static isAllowedEnumOption(key: string, value: any) {
const options = this.enumOptions(key); const options = this.enumOptions(key);
return !!options[value]; return !!options[value];
} }
@ -2262,7 +2262,7 @@ class Setting extends BaseModel {
// { sync.5.path: 'http://example', sync.5.username: 'testing' } // { sync.5.path: 'http://example', sync.5.username: 'testing' }
// and baseKey is 'sync.5', the function will return // and baseKey is 'sync.5', the function will return
// { path: 'http://example', username: 'testing' } // { path: 'http://example', username: 'testing' }
static subValues(baseKey: string, settings: any, options: any = null) { public static subValues(baseKey: string, settings: any, options: any = null) {
const includeBaseKeyInName = !!options && !!options.includeBaseKeyInName; const includeBaseKeyInName = !!options && !!options.includeBaseKeyInName;
const output: any = {}; const output: any = {};
@ -2358,7 +2358,7 @@ class Setting extends BaseModel {
logger.debug('Settings have been saved.'); logger.debug('Settings have been saved.');
} }
static scheduleChangeEvent() { public static scheduleChangeEvent() {
if (this.changeEventTimeoutId_) shim.clearTimeout(this.changeEventTimeoutId_); if (this.changeEventTimeoutId_) shim.clearTimeout(this.changeEventTimeoutId_);
this.changeEventTimeoutId_ = shim.setTimeout(() => { this.changeEventTimeoutId_ = shim.setTimeout(() => {
@ -2366,7 +2366,7 @@ class Setting extends BaseModel {
}, 1000); }, 1000);
} }
static cancelScheduleChangeEvent() { public static cancelScheduleChangeEvent() {
if (this.changeEventTimeoutId_) shim.clearTimeout(this.changeEventTimeoutId_); if (this.changeEventTimeoutId_) shim.clearTimeout(this.changeEventTimeoutId_);
this.changeEventTimeoutId_ = null; this.changeEventTimeoutId_ = null;
} }
@ -2388,7 +2388,7 @@ class Setting extends BaseModel {
eventManager.emit('settingsChange', { keys }); eventManager.emit('settingsChange', { keys });
} }
static scheduleSave() { public static scheduleSave() {
if (!Setting.autoSaveEnabled) return; if (!Setting.autoSaveEnabled) return;
if (this.saveTimeoutId_) shim.clearTimeout(this.saveTimeoutId_); if (this.saveTimeoutId_) shim.clearTimeout(this.saveTimeoutId_);
@ -2402,12 +2402,12 @@ class Setting extends BaseModel {
}, 500); }, 500);
} }
static cancelScheduleSave() { public static cancelScheduleSave() {
if (this.saveTimeoutId_) shim.clearTimeout(this.saveTimeoutId_); if (this.saveTimeoutId_) shim.clearTimeout(this.saveTimeoutId_);
this.saveTimeoutId_ = null; this.saveTimeoutId_ = null;
} }
static publicSettings(appType: AppType) { public static publicSettings(appType: AppType) {
if (!appType) throw new Error('appType is required'); if (!appType) throw new Error('appType is required');
const metadata = this.metadata(); const metadata = this.metadata();
@ -2424,7 +2424,7 @@ class Setting extends BaseModel {
return output; return output;
} }
static typeToString(typeId: number) { public static typeToString(typeId: number) {
if (typeId === SettingItemType.Int) return 'int'; if (typeId === SettingItemType.Int) return 'int';
if (typeId === SettingItemType.String) return 'string'; if (typeId === SettingItemType.String) return 'string';
if (typeId === SettingItemType.Bool) return 'bool'; if (typeId === SettingItemType.Bool) return 'bool';
@ -2438,7 +2438,7 @@ class Setting extends BaseModel {
return SettingSectionSource.Default; return SettingSectionSource.Default;
} }
static groupMetadatasBySections(metadatas: SettingItem[]) { public static groupMetadatasBySections(metadatas: SettingItem[]) {
const sections = []; const sections = [];
const generalSection: any = { name: 'general', metadatas: [] }; const generalSection: any = { name: 'general', metadatas: [] };
const nameToSections: any = {}; const nameToSections: any = {};
@ -2472,7 +2472,7 @@ class Setting extends BaseModel {
return sections; return sections;
} }
static sectionNameToLabel(name: string) { public static sectionNameToLabel(name: string) {
if (name === 'general') return _('General'); if (name === 'general') return _('General');
if (name === 'sync') return _('Synchronisation'); if (name === 'sync') return _('Synchronisation');
if (name === 'appearance') return _('Appearance'); if (name === 'appearance') return _('Appearance');
@ -2491,7 +2491,7 @@ class Setting extends BaseModel {
return name; return name;
} }
static sectionDescription(name: string) { public static sectionDescription(name: string) {
if (name === 'markdownPlugins') return _('These plugins enhance the Markdown renderer with additional features. Please note that, while these features might be useful, they are not standard Markdown and thus most of them will only work in Joplin. Additionally, some of them are *incompatible* with the WYSIWYG editor. If you open a note that uses one of these plugins in that editor, you will lose the plugin formatting. It is indicated below which plugins are compatible or not with the WYSIWYG editor.'); if (name === 'markdownPlugins') return _('These plugins enhance the Markdown renderer with additional features. Please note that, while these features might be useful, they are not standard Markdown and thus most of them will only work in Joplin. Additionally, some of them are *incompatible* with the WYSIWYG editor. If you open a note that uses one of these plugins in that editor, you will lose the plugin formatting. It is indicated below which plugins are compatible or not with the WYSIWYG editor.');
if (name === 'general') return _('Notes and settings are stored in: %s', toSystemSlashes(this.value('profileDir'), process.platform)); if (name === 'general') return _('Notes and settings are stored in: %s', toSystemSlashes(this.value('profileDir'), process.platform));
@ -2500,7 +2500,7 @@ class Setting extends BaseModel {
return ''; return '';
} }
static sectionNameToIcon(name: string) { public static sectionNameToIcon(name: string) {
if (name === 'general') return 'icon-general'; if (name === 'general') return 'icon-general';
if (name === 'sync') return 'icon-sync'; if (name === 'sync') return 'icon-sync';
if (name === 'appearance') return 'icon-appearance'; if (name === 'appearance') return 'icon-appearance';
@ -2519,7 +2519,7 @@ class Setting extends BaseModel {
return 'fas fa-cog'; return 'fas fa-cog';
} }
static appTypeToLabel(name: string) { public static appTypeToLabel(name: string) {
// Not translated for now because only used on Welcome notes (which are not translated) // Not translated for now because only used on Welcome notes (which are not translated)
if (name === 'cli') return 'CLI'; if (name === 'cli') return 'CLI';
return name[0].toUpperCase() + name.substr(1).toLowerCase(); return name[0].toUpperCase() + name.substr(1).toLowerCase();

View File

@ -3,11 +3,11 @@
import BaseModel from '../BaseModel'; import BaseModel from '../BaseModel';
export default class SmartFilter extends BaseModel { export default class SmartFilter extends BaseModel {
static tableName(): string { public static tableName(): string {
throw new Error('Not using database'); throw new Error('Not using database');
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_SMART_FILTER; return BaseModel.TYPE_SMART_FILTER;
} }
} }

View File

@ -7,15 +7,15 @@ import Note from './Note';
import { _ } from '../locale'; import { _ } from '../locale';
export default class Tag extends BaseItem { export default class Tag extends BaseItem {
static tableName() { public static tableName() {
return 'tags'; return 'tags';
} }
static modelType() { public static modelType() {
return BaseModel.TYPE_TAG; return BaseModel.TYPE_TAG;
} }
static async noteIds(tagId: string) { public static async noteIds(tagId: string) {
const rows = await this.db().selectAll('SELECT note_id FROM note_tags WHERE tag_id = ?', [tagId]); const rows = await this.db().selectAll('SELECT note_id FROM note_tags WHERE tag_id = ?', [tagId]);
const output = []; const output = [];
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@ -24,7 +24,7 @@ export default class Tag extends BaseItem {
return output; return output;
} }
static async notes(tagId: string, options: any = null) { public static async notes(tagId: string, options: any = null) {
if (options === null) options = {}; if (options === null) options = {};
const noteIds = await this.noteIds(tagId); const noteIds = await this.noteIds(tagId);
@ -39,7 +39,7 @@ export default class Tag extends BaseItem {
} }
// Untag all the notes and delete tag // Untag all the notes and delete tag
static async untagAll(tagId: string) { public static async untagAll(tagId: string) {
const noteTags = await NoteTag.modelSelectAll('SELECT id FROM note_tags WHERE tag_id = ?', [tagId]); const noteTags = await NoteTag.modelSelectAll('SELECT id FROM note_tags WHERE tag_id = ?', [tagId]);
for (let i = 0; i < noteTags.length; i++) { for (let i = 0; i < noteTags.length; i++) {
await NoteTag.delete(noteTags[i].id); await NoteTag.delete(noteTags[i].id);
@ -48,7 +48,7 @@ export default class Tag extends BaseItem {
await Tag.delete(tagId); await Tag.delete(tagId);
} }
static async delete(id: string, options: any = null) { public static async delete(id: string, options: any = null) {
if (!options) options = {}; if (!options) options = {};
await super.delete(id, options); await super.delete(id, options);
@ -59,7 +59,7 @@ export default class Tag extends BaseItem {
}); });
} }
static async addNote(tagId: string, noteId: string) { public static async addNote(tagId: string, noteId: string) {
const hasIt = await this.hasNote(tagId, noteId); const hasIt = await this.hasNote(tagId, noteId);
if (hasIt) return; if (hasIt) return;
@ -89,7 +89,7 @@ export default class Tag extends BaseItem {
return output; return output;
} }
static async removeNote(tagId: string, noteId: string) { public static async removeNote(tagId: string, noteId: string) {
const noteTags = await NoteTag.modelSelectAll('SELECT id FROM note_tags WHERE tag_id = ? and note_id = ?', [tagId, noteId]); const noteTags = await NoteTag.modelSelectAll('SELECT id FROM note_tags WHERE tag_id = ? and note_id = ?', [tagId, noteId]);
for (let i = 0; i < noteTags.length; i++) { for (let i = 0; i < noteTags.length; i++) {
await NoteTag.delete(noteTags[i].id); await NoteTag.delete(noteTags[i].id);
@ -101,34 +101,34 @@ export default class Tag extends BaseItem {
}); });
} }
static loadWithCount(tagId: string) { public static loadWithCount(tagId: string) {
const sql = 'SELECT * FROM tags_with_note_count WHERE id = ?'; const sql = 'SELECT * FROM tags_with_note_count WHERE id = ?';
return this.modelSelectOne(sql, [tagId]); return this.modelSelectOne(sql, [tagId]);
} }
static async hasNote(tagId: string, noteId: string) { public static async hasNote(tagId: string, noteId: string) {
const r = await this.db().selectOne('SELECT note_id FROM note_tags WHERE tag_id = ? AND note_id = ? LIMIT 1', [tagId, noteId]); const r = await this.db().selectOne('SELECT note_id FROM note_tags WHERE tag_id = ? AND note_id = ? LIMIT 1', [tagId, noteId]);
return !!r; return !!r;
} }
static async allWithNotes() { public static async allWithNotes() {
return await Tag.modelSelectAll('SELECT * FROM tags_with_note_count'); return await Tag.modelSelectAll('SELECT * FROM tags_with_note_count');
} }
static async searchAllWithNotes(options: any) { public static async searchAllWithNotes(options: any) {
if (!options) options = {}; if (!options) options = {};
if (!options.conditions) options.conditions = []; if (!options.conditions) options.conditions = [];
options.conditions.push('id IN (SELECT distinct id FROM tags_with_note_count)'); options.conditions.push('id IN (SELECT distinct id FROM tags_with_note_count)');
return this.search(options); return this.search(options);
} }
static async tagsByNoteId(noteId: string) { public static async tagsByNoteId(noteId: string) {
const tagIds = await NoteTag.tagIdsByNoteId(noteId); const tagIds = await NoteTag.tagIdsByNoteId(noteId);
if (!tagIds.length) return []; if (!tagIds.length) return [];
return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ("${tagIds.join('","')}")`); return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ("${tagIds.join('","')}")`);
} }
static async commonTagsByNoteIds(noteIds: string[]) { public static async commonTagsByNoteIds(noteIds: string[]) {
if (!noteIds || noteIds.length === 0) { if (!noteIds || noteIds.length === 0) {
return []; return [];
} }
@ -143,17 +143,17 @@ export default class Tag extends BaseItem {
return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ("${commonTagIds.join('","')}")`); return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ("${commonTagIds.join('","')}")`);
} }
static async loadByTitle(title: string) { public static async loadByTitle(title: string) {
return this.loadByField('title', title, { caseInsensitive: true }); return this.loadByField('title', title, { caseInsensitive: true });
} }
static async addNoteTagByTitle(noteId: string, tagTitle: string) { public static async addNoteTagByTitle(noteId: string, tagTitle: string) {
let tag = await this.loadByTitle(tagTitle); let tag = await this.loadByTitle(tagTitle);
if (!tag) tag = await Tag.save({ title: tagTitle }, { userSideValidation: true }); if (!tag) tag = await Tag.save({ title: tagTitle }, { userSideValidation: true });
return await this.addNote(tag.id, noteId); return await this.addNote(tag.id, noteId);
} }
static async setNoteTagsByTitles(noteId: string, tagTitles: string[]) { public static async setNoteTagsByTitles(noteId: string, tagTitles: string[]) {
const previousTags = await this.tagsByNoteId(noteId); const previousTags = await this.tagsByNoteId(noteId);
const addedTitles = []; const addedTitles = [];
@ -173,7 +173,7 @@ export default class Tag extends BaseItem {
} }
} }
static async setNoteTagsByIds(noteId: string, tagIds: string[]) { public static async setNoteTagsByIds(noteId: string, tagIds: string[]) {
const previousTags = await this.tagsByNoteId(noteId); const previousTags = await this.tagsByNoteId(noteId);
const addedIds = []; const addedIds = [];
@ -190,7 +190,7 @@ export default class Tag extends BaseItem {
} }
} }
static async save(o: TagEntity, options: any = null) { public static async save(o: TagEntity, options: any = null) {
options = Object.assign({}, { options = Object.assign({}, {
dispatchUpdateAction: true, dispatchUpdateAction: true,
userSideValidation: false, userSideValidation: false,

View File

@ -22,7 +22,7 @@ export default class OneDriveApi {
// apps are considered "public"), in which case the secret should not be sent to the API. // apps are considered "public"), in which case the secret should not be sent to the API.
// In practice the React Native app is public, and the Node one is not because we // In practice the React Native app is public, and the Node one is not because we
// use a local server for the OAuth dance. // use a local server for the OAuth dance.
constructor(clientId: string, clientSecret: string, isPublic: boolean) { public constructor(clientId: string, clientSecret: string, isPublic: boolean) {
this.clientId_ = clientId; this.clientId_ = clientId;
this.clientSecret_ = clientSecret; this.clientSecret_ = clientSecret;
this.auth_ = null; this.auth_ = null;
@ -33,57 +33,57 @@ export default class OneDriveApi {
}; };
} }
isPublic() { public isPublic() {
return this.isPublic_; return this.isPublic_;
} }
dispatch(eventName: string, param: any) { public dispatch(eventName: string, param: any) {
const ls = this.listeners_[eventName]; const ls = this.listeners_[eventName];
for (let i = 0; i < ls.length; i++) { for (let i = 0; i < ls.length; i++) {
ls[i](param); ls[i](param);
} }
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
this.listeners_[eventName].push(callback); this.listeners_[eventName].push(callback);
} }
tokenBaseUrl() { public tokenBaseUrl() {
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
} }
nativeClientRedirectUrl() { public nativeClientRedirectUrl() {
return 'https://login.microsoftonline.com/common/oauth2/nativeclient'; return 'https://login.microsoftonline.com/common/oauth2/nativeclient';
} }
auth(): any { public auth(): any {
return this.auth_; return this.auth_;
} }
setAuth(auth: any) { public setAuth(auth: any) {
this.auth_ = auth; this.auth_ = auth;
this.dispatch('authRefreshed', this.auth()); this.dispatch('authRefreshed', this.auth());
} }
token() { public token() {
return this.auth_ ? this.auth_.access_token : null; return this.auth_ ? this.auth_.access_token : null;
} }
clientId() { public clientId() {
return this.clientId_; return this.clientId_;
} }
clientSecret() { public clientSecret() {
return this.clientSecret_; return this.clientSecret_;
} }
async appDirectory() { public async appDirectory() {
const driveId = this.accountProperties_.driveId; const driveId = this.accountProperties_.driveId;
const r = await this.execJson('GET', `/me/drives/${driveId}/special/approot`); const r = await this.execJson('GET', `/me/drives/${driveId}/special/approot`);
return `${r.parentReference.path}/${r.name}`; return `${r.parentReference.path}/${r.name}`;
} }
authCodeUrl(redirectUri: string) { public authCodeUrl(redirectUri: string) {
const query = { const query = {
client_id: this.clientId_, client_id: this.clientId_,
scope: 'files.readwrite offline_access sites.readwrite.all', scope: 'files.readwrite offline_access sites.readwrite.all',
@ -94,7 +94,7 @@ export default class OneDriveApi {
return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`; return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`;
} }
async execTokenRequest(code: string, redirectUri: string) { public async execTokenRequest(code: string, redirectUri: string) {
const body: any = {}; const body: any = {};
body['client_id'] = this.clientId(); body['client_id'] = this.clientId();
if (!this.isPublic()) body['client_secret'] = this.clientSecret(); if (!this.isPublic()) body['client_secret'] = this.clientSecret();
@ -126,7 +126,7 @@ export default class OneDriveApi {
} }
} }
oneDriveErrorResponseToError(errorResponse: any) { public oneDriveErrorResponseToError(errorResponse: any) {
if (!errorResponse) return new Error('Undefined error'); if (!errorResponse) return new Error('Undefined error');
if (errorResponse.error) { if (errorResponse.error) {
@ -140,7 +140,7 @@ export default class OneDriveApi {
} }
} }
async uploadChunk(url: string, handle: any, buffer: any, options: any) { public async uploadChunk(url: string, handle: any, buffer: any, options: any) {
options = Object.assign({}, options); options = Object.assign({}, options);
if (!options.method) { options.method = 'POST'; } if (!options.method) { options.method = 'POST'; }
@ -162,7 +162,7 @@ export default class OneDriveApi {
return response; return response;
} }
async uploadBigFile(url: string, options: any) { public async uploadBigFile(url: string, options: any) {
const response = await shim.fetch(url, { const response = await shim.fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -227,7 +227,7 @@ export default class OneDriveApi {
} }
} }
async exec(method: string, path: string, query: any = null, data: any = null, options: any = null) { public async exec(method: string, path: string, query: any = null, data: any = null, options: any = null) {
if (!path) throw new Error('Path is required'); if (!path) throw new Error('Path is required');
method = method.toUpperCase(); method = method.toUpperCase();
@ -367,11 +367,11 @@ export default class OneDriveApi {
throw new Error(`Could not execute request after multiple attempts: ${method} ${url}`); throw new Error(`Could not execute request after multiple attempts: ${method} ${url}`);
} }
setAccountProperties(accountProperties: any) { public setAccountProperties(accountProperties: any) {
this.accountProperties_ = accountProperties; this.accountProperties_ = accountProperties;
} }
async execAccountPropertiesRequest() { public async execAccountPropertiesRequest() {
try { try {
const response = await this.exec('GET', 'https://graph.microsoft.com/v1.0/me/drive'); const response = await this.exec('GET', 'https://graph.microsoft.com/v1.0/me/drive');
@ -383,7 +383,7 @@ export default class OneDriveApi {
} }
} }
async execJson(method: string, path: string, query: any = null, data: any = null) { public async execJson(method: string, path: string, query: any = null, data: any = null) {
const response = await this.exec(method, path, query, data); const response = await this.exec(method, path, query, data);
const errorResponseText = await response.text(); const errorResponseText = await response.text();
try { try {
@ -396,13 +396,13 @@ export default class OneDriveApi {
} }
} }
async execText(method: string, path: string, query: any = null, data: any = null) { public async execText(method: string, path: string, query: any = null, data: any = null) {
const response = await this.exec(method, path, query, data); const response = await this.exec(method, path, query, data);
const output = await response.text(); const output = await response.text();
return output; return output;
} }
async refreshAccessToken() { public async refreshAccessToken() {
if (!this.auth_ || !this.auth_.refresh_token) { if (!this.auth_ || !this.auth_.refresh_token) {
this.setAuth(null); this.setAuth(null);
throw new Error(_('Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.')); throw new Error(_('Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.'));

View File

@ -17,7 +17,7 @@ class Registry {
private db_: any; private db_: any;
private isOnMobileData_ = false; private isOnMobileData_ = false;
logger() { public logger() {
if (!this.logger_) { if (!this.logger_) {
// console.warn('Calling logger before it is initialized'); // console.warn('Calling logger before it is initialized');
return new Logger(); return new Logger();
@ -26,35 +26,35 @@ class Registry {
return this.logger_; return this.logger_;
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
setShowErrorMessageBoxHandler(v: any) { public setShowErrorMessageBoxHandler(v: any) {
this.showErrorMessageBoxHandler_ = v; this.showErrorMessageBoxHandler_ = v;
} }
showErrorMessageBox(message: string) { public showErrorMessageBox(message: string) {
if (!this.showErrorMessageBoxHandler_) return; if (!this.showErrorMessageBoxHandler_) return;
this.showErrorMessageBoxHandler_(message); this.showErrorMessageBoxHandler_(message);
} }
// If isOnMobileData is true, the doWifiConnectionCheck is not set // If isOnMobileData is true, the doWifiConnectionCheck is not set
// and the sync.mobileWifiOnly setting is true it will cancel the sync. // and the sync.mobileWifiOnly setting is true it will cancel the sync.
setIsOnMobileData(isOnMobileData: boolean) { public setIsOnMobileData(isOnMobileData: boolean) {
this.isOnMobileData_ = isOnMobileData; this.isOnMobileData_ = isOnMobileData;
} }
resetSyncTarget(syncTargetId: number = null) { public resetSyncTarget(syncTargetId: number = null) {
if (syncTargetId === null) syncTargetId = Setting.value('sync.target'); if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
delete this.syncTargets_[syncTargetId]; delete this.syncTargets_[syncTargetId];
} }
syncTargetNextcloud() { public syncTargetNextcloud() {
return this.syncTarget(SyncTargetRegistry.nameToId('nextcloud')); return this.syncTarget(SyncTargetRegistry.nameToId('nextcloud'));
} }
syncTarget = (syncTargetId: number = null) => { public syncTarget = (syncTargetId: number = null) => {
if (syncTargetId === null) syncTargetId = Setting.value('sync.target'); if (syncTargetId === null) syncTargetId = Setting.value('sync.target');
if (this.syncTargets_[syncTargetId]) return this.syncTargets_[syncTargetId]; if (this.syncTargets_[syncTargetId]) return this.syncTargets_[syncTargetId];
@ -70,7 +70,7 @@ class Registry {
// This can be used when some data has been modified and we want to make // This can be used when some data has been modified and we want to make
// sure it gets synced. So we wait for the current sync operation to // sure it gets synced. So we wait for the current sync operation to
// finish (if one is running), then we trigger a sync just after. // finish (if one is running), then we trigger a sync just after.
waitForSyncFinishedThenSync = async () => { public waitForSyncFinishedThenSync = async () => {
if (!Setting.value('sync.target')) { if (!Setting.value('sync.target')) {
this.logger().info('waitForSyncFinishedThenSync - cancelling because no sync target is selected.'); this.logger().info('waitForSyncFinishedThenSync - cancelling because no sync target is selected.');
return; return;
@ -86,7 +86,7 @@ class Registry {
} }
}; };
scheduleSync = async (delay: number = null, syncOptions: any = null, doWifiConnectionCheck: boolean = false) => { public scheduleSync = async (delay: number = null, syncOptions: any = null, doWifiConnectionCheck: boolean = false) => {
this.schedSyncCalls_.push(true); this.schedSyncCalls_.push(true);
try { try {
@ -194,7 +194,7 @@ class Registry {
} }
}; };
setupRecurrentSync() { public setupRecurrentSync() {
this.setupRecurrentCalls_.push(true); this.setupRecurrentCalls_.push(true);
try { try {
@ -223,15 +223,15 @@ class Registry {
} }
} }
setDb = (v: any) => { public setDb = (v: any) => {
this.db_ = v; this.db_ = v;
}; };
db() { public db() {
return this.db_; return this.db_;
} }
cancelTimers_() { private cancelTimers_() {
if (this.recurrentSyncId_) { if (this.recurrentSyncId_) {
shim.clearInterval(this.recurrentSyncId_); shim.clearInterval(this.recurrentSyncId_);
this.recurrentSyncId_ = null; this.recurrentSyncId_ = null;
@ -242,7 +242,7 @@ class Registry {
} }
} }
cancelTimers = async () => { public cancelTimers = async () => {
this.logger().info('Cancelling sync timers'); this.logger().info('Cancelling sync timers');
this.cancelTimers_(); this.cancelTimers_();

View File

@ -9,31 +9,31 @@ export default class AlarmService {
private static logger_: Logger; private static logger_: Logger;
// private static inAppNotificationHandler_:any; // private static inAppNotificationHandler_:any;
static setDriver(v: any) { public static setDriver(v: any) {
this.driver_ = v; this.driver_ = v;
if (this.driver_.setService) this.driver_.setService(this); if (this.driver_.setService) this.driver_.setService(this);
} }
static driver() { public static driver() {
if (!this.driver_) throw new Error('AlarmService driver not set!'); if (!this.driver_) throw new Error('AlarmService driver not set!');
return this.driver_; return this.driver_;
} }
static setLogger(v: Logger) { public static setLogger(v: Logger) {
this.logger_ = v; this.logger_ = v;
} }
static logger() { public static logger() {
return this.logger_; return this.logger_;
} }
static setInAppNotificationHandler(v: any) { public static setInAppNotificationHandler(v: any) {
// this.inAppNotificationHandler_ = v; // this.inAppNotificationHandler_ = v;
if (this.driver_.setInAppNotificationHandler) this.driver_.setInAppNotificationHandler(v); if (this.driver_.setInAppNotificationHandler) this.driver_.setInAppNotificationHandler(v);
} }
static async garbageCollect() { public static async garbageCollect() {
this.logger().info('Garbage collecting alarms...'); this.logger().info('Garbage collecting alarms...');
// Delete alarms that have already been triggered // Delete alarms that have already been triggered
@ -50,7 +50,7 @@ export default class AlarmService {
// When passing a note, make sure it has all the required properties // When passing a note, make sure it has all the required properties
// (better to pass a complete note or else just the ID) // (better to pass a complete note or else just the ID)
static async updateNoteNotification(noteOrId: any, isDeleted: boolean = false) { public static async updateNoteNotification(noteOrId: any, isDeleted: boolean = false) {
try { try {
let note = null; let note = null;
let noteId = null; let noteId = null;
@ -114,7 +114,7 @@ export default class AlarmService {
} }
} }
static async updateAllNotifications() { public static async updateAllNotifications() {
this.logger().info('Updating all notifications...'); this.logger().info('Updating all notifications...');
await this.garbageCollect(); await this.garbageCollect();

View File

@ -14,29 +14,29 @@ export default class AlarmServiceDriverNode {
private notifications_: any = {}; private notifications_: any = {};
private service_: any = null; private service_: any = null;
constructor(options: Options) { public constructor(options: Options) {
// Note: appName is required to get the notification to work. It must be the same as the appId defined in package.json // Note: appName is required to get the notification to work. It must be the same as the appId defined in package.json
// https://github.com/mikaelbr/node-notifier/issues/144#issuecomment-319324058 // https://github.com/mikaelbr/node-notifier/issues/144#issuecomment-319324058
this.appName_ = options.appName; this.appName_ = options.appName;
} }
setService(s: any) { public setService(s: any) {
this.service_ = s; this.service_ = s;
} }
logger() { public logger() {
return this.service_.logger(); return this.service_.logger();
} }
hasPersistentNotifications() { public hasPersistentNotifications() {
return false; return false;
} }
notificationIsSet(id: number) { public notificationIsSet(id: number) {
return id in this.notifications_; return id in this.notifications_;
} }
clearNotification(id: number) { public clearNotification(id: number) {
if (!this.notificationIsSet(id)) return; if (!this.notificationIsSet(id)) return;
shim.clearTimeout(this.notifications_[id].timeoutId); shim.clearTimeout(this.notifications_[id].timeoutId);
delete this.notifications_[id]; delete this.notifications_[id];
@ -126,7 +126,7 @@ export default class AlarmServiceDriverNode {
return 'granted'; return 'granted';
} }
async scheduleNotification(notification: Notification) { public async scheduleNotification(notification: Notification) {
const now = Date.now(); const now = Date.now();
const interval = notification.date.getTime() - now; const interval = notification.date.getTime() - now;
if (interval < 0) return; if (interval < 0) return;

View File

@ -2,16 +2,16 @@ import Logger from '../Logger';
export default class BaseService { export default class BaseService {
static logger_: Logger = null; public static logger_: Logger = null;
protected instanceLogger_: Logger = null; protected instanceLogger_: Logger = null;
logger(): Logger { public logger(): Logger {
if (this.instanceLogger_) return this.instanceLogger_; if (this.instanceLogger_) return this.instanceLogger_;
if (!BaseService.logger_) throw new Error('BaseService.logger_ not set!!'); if (!BaseService.logger_) throw new Error('BaseService.logger_ not set!!');
return BaseService.logger_; return BaseService.logger_;
} }
setLogger(v: Logger) { public setLogger(v: Logger) {
this.instanceLogger_ = v; this.instanceLogger_ = v;
} }
} }

View File

@ -31,53 +31,53 @@ export default class DecryptionWorker {
private startCalls_: boolean[] = []; private startCalls_: boolean[] = [];
private encryptionService_: EncryptionService = null; private encryptionService_: EncryptionService = null;
constructor() { public constructor() {
this.state_ = 'idle'; this.state_ = 'idle';
this.logger_ = new Logger(); this.logger_ = new Logger();
this.eventEmitter_ = new EventEmitter(); this.eventEmitter_ = new EventEmitter();
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.eventEmitter_.on(eventName, callback); return this.eventEmitter_.on(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.eventEmitter_.removeListener(eventName, callback); return this.eventEmitter_.removeListener(eventName, callback);
} }
static instance() { public static instance() {
if (DecryptionWorker.instance_) return DecryptionWorker.instance_; if (DecryptionWorker.instance_) return DecryptionWorker.instance_;
DecryptionWorker.instance_ = new DecryptionWorker(); DecryptionWorker.instance_ = new DecryptionWorker();
return DecryptionWorker.instance_; return DecryptionWorker.instance_;
} }
setEncryptionService(v: any) { public setEncryptionService(v: any) {
this.encryptionService_ = v; this.encryptionService_ = v;
} }
setKvStore(v: KvStore) { public setKvStore(v: KvStore) {
this.kvStore_ = v; this.kvStore_ = v;
} }
encryptionService() { public encryptionService() {
if (!this.encryptionService_) throw new Error('DecryptionWorker.encryptionService_ is not set!!'); if (!this.encryptionService_) throw new Error('DecryptionWorker.encryptionService_ is not set!!');
return this.encryptionService_; return this.encryptionService_;
} }
kvStore() { public kvStore() {
if (!this.kvStore_) throw new Error('DecryptionWorker.kvStore_ is not set!!'); if (!this.kvStore_) throw new Error('DecryptionWorker.kvStore_ is not set!!');
return this.kvStore_; return this.kvStore_;
} }
async scheduleStart() { public async scheduleStart() {
if (this.scheduleId_) return; if (this.scheduleId_) return;
this.scheduleId_ = shim.setTimeout(() => { this.scheduleId_ = shim.setTimeout(() => {
@ -88,7 +88,7 @@ export default class DecryptionWorker {
}, 1000); }, 1000);
} }
async decryptionDisabledItems() { public async decryptionDisabledItems() {
let items = await this.kvStore().searchByPrefix('decrypt:'); let items = await this.kvStore().searchByPrefix('decrypt:');
items = items.filter(item => item.value > this.maxDecryptionAttempts_); items = items.filter(item => item.value > this.maxDecryptionAttempts_);
items = items.map(item => { items = items.map(item => {
@ -101,15 +101,15 @@ export default class DecryptionWorker {
return items; return items;
} }
async clearDisabledItem(typeId: string, itemId: string) { public async clearDisabledItem(typeId: string, itemId: string) {
await this.kvStore().deleteValue(`decrypt:${typeId}:${itemId}`); await this.kvStore().deleteValue(`decrypt:${typeId}:${itemId}`);
} }
async clearDisabledItems() { public async clearDisabledItems() {
await this.kvStore().deleteByPrefix('decrypt:'); await this.kvStore().deleteByPrefix('decrypt:');
} }
dispatchReport(report: any) { public dispatchReport(report: any) {
const action = Object.assign({}, report); const action = Object.assign({}, report);
action.type = 'DECRYPTION_WORKER_SET'; action.type = 'DECRYPTION_WORKER_SET';
this.dispatch(action); this.dispatch(action);
@ -301,7 +301,7 @@ export default class DecryptionWorker {
return output; return output;
} }
async destroy() { public async destroy() {
this.eventEmitter_.removeAllListeners(); this.eventEmitter_.removeAllListeners();
if (this.scheduleId_) { if (this.scheduleId_) {
shim.clearTimeout(this.scheduleId_); shim.clearTimeout(this.scheduleId_);

View File

@ -55,27 +55,27 @@ export default class ExternalEditWatcher {
}; };
} }
tempDir() { public tempDir() {
return Setting.value('profileDir'); return Setting.value('profileDir');
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.eventEmitter_.on(eventName, callback); return this.eventEmitter_.on(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.eventEmitter_.removeListener(eventName, callback); return this.eventEmitter_.removeListener(eventName, callback);
} }
setLogger(l: Logger) { public setLogger(l: Logger) {
this.logger_ = l; this.logger_ = l;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
watch(fileToWatch: string) { public watch(fileToWatch: string) {
if (!this.chokidar_) return; if (!this.chokidar_) return;
if (!this.watcher_) { if (!this.watcher_) {
@ -164,11 +164,11 @@ export default class ExternalEditWatcher {
return this.watcher_; return this.watcher_;
} }
noteIdToFilePath_(noteId: string) { private noteIdToFilePath_(noteId: string) {
return `${this.tempDir()}/edit-${noteId}.md`; return `${this.tempDir()}/edit-${noteId}.md`;
} }
noteFilePathToId_(path: string) { private noteFilePathToId_(path: string) {
let id: any = toSystemSlashes(path, 'linux').split('/'); let id: any = toSystemSlashes(path, 'linux').split('/');
if (!id.length) throw new Error(`Invalid path: ${path}`); if (!id.length) throw new Error(`Invalid path: ${path}`);
id = id[id.length - 1]; id = id[id.length - 1];
@ -178,7 +178,7 @@ export default class ExternalEditWatcher {
return id[1]; return id[1];
} }
watchedFiles() { public watchedFiles() {
if (!this.watcher_) return []; if (!this.watcher_) return [];
const output = []; const output = [];
@ -196,7 +196,7 @@ export default class ExternalEditWatcher {
return output; return output;
} }
noteIsWatched(note: NoteEntity) { public noteIsWatched(note: NoteEntity) {
if (!this.watcher_) return false; if (!this.watcher_) return false;
const noteFilename = basename(this.noteIdToFilePath_(note.id)); const noteFilename = basename(this.noteIdToFilePath_(note.id));
@ -215,7 +215,7 @@ export default class ExternalEditWatcher {
return false; return false;
} }
async openAndWatch(note: NoteEntity) { public async openAndWatch(note: NoteEntity) {
if (!note || !note.id) { if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot open note: ', note); this.logger().warn('ExternalEditWatcher: Cannot open note: ', note);
return; return;
@ -235,7 +235,7 @@ export default class ExternalEditWatcher {
this.logger().info(`ExternalEditWatcher: Started watching ${filePath}`); this.logger().info(`ExternalEditWatcher: Started watching ${filePath}`);
} }
async stopWatching(noteId: string) { public async stopWatching(noteId: string) {
if (!noteId) return; if (!noteId) return;
const filePath = this.noteIdToFilePath_(noteId); const filePath = this.noteIdToFilePath_(noteId);
@ -248,7 +248,7 @@ export default class ExternalEditWatcher {
this.logger().info(`ExternalEditWatcher: Stopped watching ${filePath}`); this.logger().info(`ExternalEditWatcher: Stopped watching ${filePath}`);
} }
async stopWatchingAll() { public async stopWatchingAll() {
const filePaths = this.watchedFiles(); const filePaths = this.watchedFiles();
for (let i = 0; i < filePaths.length; i++) { for (let i = 0; i < filePaths.length; i++) {
await shim.fsDriver().remove(filePaths[i]); await shim.fsDriver().remove(filePaths[i]);
@ -262,7 +262,7 @@ export default class ExternalEditWatcher {
}); });
} }
async updateNoteFile(note: NoteEntity) { public async updateNoteFile(note: NoteEntity) {
if (!this.noteIsWatched(note)) return; if (!this.noteIsWatched(note)) return;
if (!note || !note.id) { if (!note || !note.id) {
@ -279,7 +279,7 @@ export default class ExternalEditWatcher {
await this.writeNoteToFile_(note); await this.writeNoteToFile_(note);
} }
async writeNoteToFile_(note: NoteEntity) { private async writeNoteToFile_(note: NoteEntity) {
if (!note || !note.id) { if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note); this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
return null; return null;

View File

@ -4,7 +4,7 @@ import ItemChange from '../models/ItemChange';
const dayMs = 86400000; const dayMs = 86400000;
export default class ItemChangeUtils { export default class ItemChangeUtils {
static async deleteProcessedChanges(itemMinTtl: number = dayMs * 90) { public static async deleteProcessedChanges(itemMinTtl: number = dayMs * 90) {
const lastProcessedChangeIds = [ const lastProcessedChangeIds = [
Setting.value('resourceService.lastProcessedChangeId'), Setting.value('resourceService.lastProcessedChangeId'),
Setting.value('searchEngine.lastProcessedChangeId'), Setting.value('searchEngine.lastProcessedChangeId'),

View File

@ -5,18 +5,18 @@ export default class MigrationService extends BaseService {
private static instance_: MigrationService; private static instance_: MigrationService;
static instance() { public static instance() {
if (this.instance_) return this.instance_; if (this.instance_) return this.instance_;
this.instance_ = new MigrationService(); this.instance_ = new MigrationService();
return this.instance_; return this.instance_;
} }
async runScript(num: number) { public async runScript(num: number) {
const script = Migration.script(num); const script = Migration.script(num);
await script.exec(); await script.exec();
} }
async run() { public async run() {
const migrations = await Migration.migrationsToDo(); const migrations = await Migration.migrationsToDo();
for (const migration of migrations) { for (const migration of migrations) {

View File

@ -41,7 +41,7 @@ interface ReportItem {
} }
export default class ReportService { export default class ReportService {
csvEscapeCell(cell: string) { public csvEscapeCell(cell: string) {
cell = this.csvValueToString(cell); cell = this.csvValueToString(cell);
const output = cell.replace(/"/, '""'); const output = cell.replace(/"/, '""');
if (this.csvCellRequiresQuotes(cell, ',')) { if (this.csvCellRequiresQuotes(cell, ',')) {
@ -50,26 +50,26 @@ export default class ReportService {
return output; return output;
} }
csvCellRequiresQuotes(cell: string, delimiter: string) { public csvCellRequiresQuotes(cell: string, delimiter: string) {
if (cell.indexOf('\n') >= 0) return true; if (cell.indexOf('\n') >= 0) return true;
if (cell.indexOf('"') >= 0) return true; if (cell.indexOf('"') >= 0) return true;
if (cell.indexOf(delimiter) >= 0) return true; if (cell.indexOf(delimiter) >= 0) return true;
return false; return false;
} }
csvValueToString(v: string) { public csvValueToString(v: string) {
if (v === undefined || v === null) return ''; if (v === undefined || v === null) return '';
return v.toString(); return v.toString();
} }
csvCreateLine(row: string[]) { public csvCreateLine(row: string[]) {
for (let i = 0; i < row.length; i++) { for (let i = 0; i < row.length; i++) {
row[i] = this.csvEscapeCell(row[i]); row[i] = this.csvEscapeCell(row[i]);
} }
return row.join(','); return row.join(',');
} }
csvCreate(rows: any[]) { public csvCreate(rows: any[]) {
const output = []; const output = [];
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
output.push(this.csvCreateLine(rows[i])); output.push(this.csvCreateLine(rows[i]));
@ -77,7 +77,7 @@ export default class ReportService {
return output.join('\n'); return output.join('\n');
} }
async basicItemList(option: any = null) { public async basicItemList(option: any = null) {
if (!option) option = {}; if (!option) option = {};
if (!option.format) option.format = 'array'; if (!option.format) option.format = 'array';
@ -100,7 +100,7 @@ export default class ReportService {
return option.format === 'csv' ? this.csvCreate(output) : output; return option.format === 'csv' ? this.csvCreate(output) : output;
} }
async syncStatus(syncTarget: number) { public async syncStatus(syncTarget: number) {
const output: any = { const output: any = {
items: {}, items: {},
total: {}, total: {},
@ -162,7 +162,7 @@ export default class ReportService {
return section; return section;
} }
async status(syncTarget: number): Promise<ReportSection[]> { public async status(syncTarget: number): Promise<ReportSection[]> {
const r = await this.syncStatus(syncTarget); const r = await this.syncStatus(syncTarget);
const sections: ReportSection[] = []; const sections: ReportSection[] = [];
let section: ReportSection = null; let section: ReportSection = null;

View File

@ -36,7 +36,7 @@ export default class ResourceEditWatcher {
private tempDir_: string = ''; private tempDir_: string = '';
private openItem_: OpenItemFn; private openItem_: OpenItemFn;
constructor() { public constructor() {
this.logger_ = new Logger(); this.logger_ = new Logger();
this.dispatch = () => {}; this.dispatch = () => {};
this.watcher_ = null; this.watcher_ = null;
@ -44,13 +44,13 @@ export default class ResourceEditWatcher {
this.eventEmitter_ = new EventEmitter(); this.eventEmitter_ = new EventEmitter();
} }
initialize(logger: any, dispatch: Function, openItem: OpenItemFn) { public initialize(logger: any, dispatch: Function, openItem: OpenItemFn) {
this.logger_ = logger; this.logger_ = logger;
this.dispatch = dispatch; this.dispatch = dispatch;
this.openItem_ = openItem; this.openItem_ = openItem;
} }
static instance() { public static instance() {
if (this.instance_) return this.instance_; if (this.instance_) return this.instance_;
this.instance_ = new ResourceEditWatcher(); this.instance_ = new ResourceEditWatcher();
return this.instance_; return this.instance_;
@ -65,19 +65,19 @@ export default class ResourceEditWatcher {
return this.tempDir_; return this.tempDir_;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.eventEmitter_.on(eventName, callback); return this.eventEmitter_.on(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.eventEmitter_.removeListener(eventName, callback); return this.eventEmitter_.removeListener(eventName, callback);
} }
externalApi() { public externalApi() {
return { return {
openAndWatch: async ({ resourceId }: any) => { openAndWatch: async ({ resourceId }: any) => {
return this.openAndWatch(resourceId); return this.openAndWatch(resourceId);
@ -260,7 +260,7 @@ export default class ResourceEditWatcher {
this.openItem_(watchedItem.path); this.openItem_(watchedItem.path);
} }
async stopWatching(resourceId: string) { public async stopWatching(resourceId: string) {
if (!resourceId) return; if (!resourceId) return;
const item = this.watchedItemByResourceId(resourceId); const item = this.watchedItemByResourceId(resourceId);

View File

@ -24,43 +24,43 @@ export default class ResourceFetcher extends BaseService {
private scheduleQueueProcessIID_: any; private scheduleQueueProcessIID_: any;
private scheduleAutoAddResourcesIID_: any; private scheduleAutoAddResourcesIID_: any;
constructor(fileApi: any = null) { public constructor(fileApi: any = null) {
super(); super();
this.setFileApi(fileApi); this.setFileApi(fileApi);
} }
static instance() { public static instance() {
if (ResourceFetcher.instance_) return ResourceFetcher.instance_; if (ResourceFetcher.instance_) return ResourceFetcher.instance_;
ResourceFetcher.instance_ = new ResourceFetcher(); ResourceFetcher.instance_ = new ResourceFetcher();
return ResourceFetcher.instance_; return ResourceFetcher.instance_;
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.eventEmitter_.on(eventName, callback); return this.eventEmitter_.on(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.eventEmitter_.removeListener(eventName, callback); return this.eventEmitter_.removeListener(eventName, callback);
} }
setLogger(logger: Logger) { public setLogger(logger: Logger) {
this.logger_ = logger; this.logger_ = logger;
} }
logger() { public logger() {
return this.logger_; return this.logger_;
} }
setFileApi(v: any) { public setFileApi(v: any) {
if (v !== null && typeof v !== 'function') throw new Error(`fileApi must be a function that returns the API. Type is ${typeof v}`); if (v !== null && typeof v !== 'function') throw new Error(`fileApi must be a function that returns the API. Type is ${typeof v}`);
this.fileApi_ = v; this.fileApi_ = v;
} }
async fileApi() { public async fileApi() {
return this.fileApi_(); return this.fileApi_();
} }
queuedItemIndex_(resourceId: string) { private queuedItemIndex_(resourceId: string) {
for (let i = 0; i < this.fetchingItems_.length; i++) { for (let i = 0; i < this.fetchingItems_.length; i++) {
const item = this.fetchingItems_[i]; const item = this.fetchingItems_[i];
if (item.id === resourceId) return i; if (item.id === resourceId) return i;
@ -68,7 +68,7 @@ export default class ResourceFetcher extends BaseService {
return -1; return -1;
} }
updateReport() { public updateReport() {
const fetchingCount = Object.keys(this.fetchingItems_).length; const fetchingCount = Object.keys(this.fetchingItems_).length;
this.dispatch({ this.dispatch({
type: 'RESOURCE_FETCHER_SET', type: 'RESOURCE_FETCHER_SET',
@ -77,7 +77,7 @@ export default class ResourceFetcher extends BaseService {
}); });
} }
async markForDownload(resourceIds: string[]) { public async markForDownload(resourceIds: string[]) {
if (!Array.isArray(resourceIds)) resourceIds = [resourceIds]; if (!Array.isArray(resourceIds)) resourceIds = [resourceIds];
const fetchStatuses = await Resource.fetchStatuses(resourceIds); const fetchStatuses = await Resource.fetchStatuses(resourceIds);
@ -97,7 +97,7 @@ export default class ResourceFetcher extends BaseService {
} }
} }
queueDownload_(resourceId: string, priority: string = null) { public queueDownload_(resourceId: string, priority: string = null) {
if (priority === null) priority = 'normal'; if (priority === null) priority = 'normal';
const index = this.queuedItemIndex_(resourceId); const index = this.queuedItemIndex_(resourceId);
@ -118,7 +118,7 @@ export default class ResourceFetcher extends BaseService {
return true; return true;
} }
async startDownload_(resourceId: string) { private async startDownload_(resourceId: string) {
if (this.fetchingItems_[resourceId]) return; if (this.fetchingItems_[resourceId]) return;
this.fetchingItems_[resourceId] = true; this.fetchingItems_[resourceId] = true;
@ -195,7 +195,7 @@ export default class ResourceFetcher extends BaseService {
}); });
} }
processQueue_() { private processQueue_() {
while (Object.getOwnPropertyNames(this.fetchingItems_).length < this.maxDownloads_) { while (Object.getOwnPropertyNames(this.fetchingItems_).length < this.maxDownloads_) {
if (!this.queue_.length) break; if (!this.queue_.length) break;
const item = this.queue_.splice(0, 1)[0]; const item = this.queue_.splice(0, 1)[0];
@ -207,7 +207,7 @@ export default class ResourceFetcher extends BaseService {
} }
} }
async waitForAllFinished() { public async waitForAllFinished() {
return new Promise((resolve) => { return new Promise((resolve) => {
const iid = shim.setInterval(() => { const iid = shim.setInterval(() => {
if (!this.updateReportIID_ && if (!this.updateReportIID_ &&
@ -223,7 +223,7 @@ export default class ResourceFetcher extends BaseService {
}); });
} }
async autoAddResources(limit: number = null) { public async autoAddResources(limit: number = null) {
this.autoAddResourcesCalls_.push(true); this.autoAddResourcesCalls_.push(true);
try { try {
if (limit === null) limit = 10; if (limit === null) limit = 10;
@ -251,12 +251,12 @@ export default class ResourceFetcher extends BaseService {
} }
} }
async start() { public async start() {
await Resource.resetStartedFetchStatus(); await Resource.resetStartedFetchStatus();
void this.autoAddResources(10); void this.autoAddResources(10);
} }
scheduleQueueProcess() { public scheduleQueueProcess() {
if (this.scheduleQueueProcessIID_) { if (this.scheduleQueueProcessIID_) {
shim.clearTimeout(this.scheduleQueueProcessIID_); shim.clearTimeout(this.scheduleQueueProcessIID_);
this.scheduleQueueProcessIID_ = null; this.scheduleQueueProcessIID_ = null;
@ -268,7 +268,7 @@ export default class ResourceFetcher extends BaseService {
}, 100); }, 100);
} }
scheduleAutoAddResources() { public scheduleAutoAddResources() {
if (this.scheduleAutoAddResourcesIID_) return; if (this.scheduleAutoAddResourcesIID_) return;
this.scheduleAutoAddResourcesIID_ = shim.setTimeout(() => { this.scheduleAutoAddResourcesIID_ = shim.setTimeout(() => {
@ -277,12 +277,12 @@ export default class ResourceFetcher extends BaseService {
}, 1000); }, 1000);
} }
async fetchAll() { public async fetchAll() {
await Resource.resetStartedFetchStatus(); await Resource.resetStartedFetchStatus();
void this.autoAddResources(null); void this.autoAddResources(null);
} }
async destroy() { public async destroy() {
this.eventEmitter_.removeAllListeners(); this.eventEmitter_.removeAllListeners();
if (this.scheduleQueueProcessIID_) { if (this.scheduleQueueProcessIID_) {
shim.clearTimeout(this.scheduleQueueProcessIID_); shim.clearTimeout(this.scheduleQueueProcessIID_);

View File

@ -32,17 +32,17 @@ export default class RevisionService extends BaseService {
private isCollecting_ = false; private isCollecting_ = false;
public isRunningInBackground_ = false; public isRunningInBackground_ = false;
static instance() { public static instance() {
if (this.instance_) return this.instance_; if (this.instance_) return this.instance_;
this.instance_ = new RevisionService(); this.instance_ = new RevisionService();
return this.instance_; return this.instance_;
} }
oldNoteCutOffDate_() { public oldNoteCutOffDate_() {
return Date.now() - Setting.value('revisionService.oldNoteInterval'); return Date.now() - Setting.value('revisionService.oldNoteInterval');
} }
async isOldNote(noteId: string) { public async isOldNote(noteId: string) {
if (noteId in this.isOldNotesCache_) return this.isOldNotesCache_[noteId]; if (noteId in this.isOldNotesCache_) return this.isOldNotesCache_[noteId];
const isOld = await Note.noteIsOlderThan(noteId, this.oldNoteCutOffDate_()); const isOld = await Note.noteIsOlderThan(noteId, this.oldNoteCutOffDate_());
@ -50,7 +50,7 @@ export default class RevisionService extends BaseService {
return isOld; return isOld;
} }
noteMetadata_(note: NoteEntity) { private noteMetadata_(note: NoteEntity) {
const excludedFields = ['type_', 'title', 'body', 'created_time', 'updated_time', 'encryption_applied', 'encryption_cipher_text', 'is_conflict']; const excludedFields = ['type_', 'title', 'body', 'created_time', 'updated_time', 'encryption_applied', 'encryption_cipher_text', 'is_conflict'];
const md: any = {}; const md: any = {};
for (const k in note) { for (const k in note) {
@ -214,7 +214,7 @@ export default class RevisionService extends BaseService {
logger.info(`collectRevisions: Created revisions for ${doneNoteIds.length} notes`); logger.info(`collectRevisions: Created revisions for ${doneNoteIds.length} notes`);
} }
async deleteOldRevisions(ttl: number) { public async deleteOldRevisions(ttl: number) {
return Revision.deleteOldRevisions(ttl); return Revision.deleteOldRevisions(ttl);
} }
@ -239,11 +239,11 @@ export default class RevisionService extends BaseService {
return output; return output;
} }
restoreFolderTitle() { public restoreFolderTitle() {
return _('Restored Notes'); return _('Restored Notes');
} }
async restoreFolder() { public async restoreFolder() {
let folder = await Folder.loadByTitle(this.restoreFolderTitle()); let folder = await Folder.loadByTitle(this.restoreFolderTitle());
if (!folder) { if (!folder) {
folder = await Folder.save({ title: this.restoreFolderTitle() }); folder = await Folder.save({ title: this.restoreFolderTitle() });
@ -267,7 +267,7 @@ export default class RevisionService extends BaseService {
return _('The note "%s" has been successfully restored to the notebook "%s".', substrWithEllipsis(note.title, 0, 32), this.restoreFolderTitle()); return _('The note "%s" has been successfully restored to the notebook "%s".', substrWithEllipsis(note.title, 0, 32), this.restoreFolderTitle());
} }
async importRevisionNote(note: NoteEntity): Promise<NoteEntity> { public async importRevisionNote(note: NoteEntity): Promise<NoteEntity> {
const toImport = Object.assign({}, note); const toImport = Object.assign({}, note);
delete toImport.id; delete toImport.id;
delete toImport.updated_time; delete toImport.updated_time;
@ -282,7 +282,7 @@ export default class RevisionService extends BaseService {
return Note.save(toImport); return Note.save(toImport);
} }
async maintenance() { public async maintenance() {
this.maintenanceCalls_.push(true); this.maintenanceCalls_.push(true);
try { try {
const startTime = Date.now(); const startTime = Date.now();
@ -308,7 +308,7 @@ export default class RevisionService extends BaseService {
} }
} }
runInBackground(collectRevisionInterval: number = null) { public runInBackground(collectRevisionInterval: number = null) {
if (this.isRunningInBackground_) return; if (this.isRunningInBackground_) return;
this.isRunningInBackground_ = true; this.isRunningInBackground_ = true;
@ -325,7 +325,7 @@ export default class RevisionService extends BaseService {
}, collectRevisionInterval); }, collectRevisionInterval);
} }
async cancelTimers() { public async cancelTimers() {
if (this.maintenanceTimer1_) { if (this.maintenanceTimer1_) {
shim.clearTimeout(this.maintenanceTimer1_); shim.clearTimeout(this.maintenanceTimer1_);
this.maintenanceTimer1_ = null; this.maintenanceTimer1_ = null;

View File

@ -6,22 +6,22 @@ class UndoQueue {
private inner_: any[] = []; private inner_: any[] = [];
private size_: number = 20; private size_: number = 20;
pop() { public pop() {
return this.inner_.pop(); return this.inner_.pop();
} }
push(e: any) { public push(e: any) {
this.inner_.push(e); this.inner_.push(e);
while (this.length > this.size_) { while (this.length > this.size_) {
this.inner_.splice(0, 1); this.inner_.splice(0, 1);
} }
} }
get length(): number { public get length(): number {
return this.inner_.length; return this.inner_.length;
} }
at(index: number): any { public at(index: number): any {
return this.inner_[index]; return this.inner_[index];
} }
@ -35,31 +35,31 @@ export default class UndoRedoService {
private eventEmitter: any = new EventEmitter(); private eventEmitter: any = new EventEmitter();
private isUndoing: boolean = false; private isUndoing: boolean = false;
constructor() { public constructor() {
this.push = this.push.bind(this); this.push = this.push.bind(this);
} }
on(eventName: string, callback: Function) { public on(eventName: string, callback: Function) {
return this.eventEmitter.on(eventName, callback); return this.eventEmitter.on(eventName, callback);
} }
off(eventName: string, callback: Function) { public off(eventName: string, callback: Function) {
return this.eventEmitter.removeListener(eventName, callback); return this.eventEmitter.removeListener(eventName, callback);
} }
push(state: any) { public push(state: any) {
this.undoStates.push(state); this.undoStates.push(state);
this.redoStates = new UndoQueue(); this.redoStates = new UndoQueue();
this.eventEmitter.emit('stackChange'); this.eventEmitter.emit('stackChange');
} }
schedulePush(state: any) { public schedulePush(state: any) {
this.pushAsyncQueue.push(async () => { this.pushAsyncQueue.push(async () => {
this.push(state); this.push(state);
}); });
} }
async undo(redoState: any) { public async undo(redoState: any) {
if (this.isUndoing) return; if (this.isUndoing) return;
if (!this.canUndo) throw new Error('Nothing to undo'); if (!this.canUndo) throw new Error('Nothing to undo');
this.isUndoing = true; this.isUndoing = true;
@ -71,7 +71,7 @@ export default class UndoRedoService {
return state; return state;
} }
async redo(undoState: any) { public async redo(undoState: any) {
if (this.isUndoing) return; if (this.isUndoing) return;
if (!this.canRedo) throw new Error('Nothing to redo'); if (!this.canRedo) throw new Error('Nothing to redo');
this.isUndoing = true; this.isUndoing = true;
@ -83,7 +83,7 @@ export default class UndoRedoService {
return state; return state;
} }
async reset() { public async reset() {
this.undoStates = new UndoQueue(); this.undoStates = new UndoQueue();
this.redoStates = new UndoQueue(); this.redoStates = new UndoQueue();
this.isUndoing = false; this.isUndoing = false;
@ -92,11 +92,11 @@ export default class UndoRedoService {
return output; return output;
} }
get canUndo(): boolean { public get canUndo(): boolean {
return !!this.undoStates.length; return !!this.undoStates.length;
} }
get canRedo(): boolean { public get canRedo(): boolean {
return !!this.redoStates.length; return !!this.redoStates.length;
} }

View File

@ -62,7 +62,7 @@ export default class MenuUtils {
private menuItemCache_: MenuItemCache = {}; private menuItemCache_: MenuItemCache = {};
private menuItemPropsCache_: MenuItemPropsCache = {}; private menuItemPropsCache_: MenuItemPropsCache = {};
constructor(service: CommandService) { public constructor(service: CommandService) {
this.service_ = service; this.service_ = service;
} }

View File

@ -26,7 +26,7 @@ export default class ToolbarButtonUtils {
private service_: CommandService; private service_: CommandService;
private toolbarButtonCache_: ToolbarButtonCache = {}; private toolbarButtonCache_: ToolbarButtonCache = {};
constructor(service: CommandService) { public constructor(service: CommandService) {
this.service_ = service; this.service_ = service;
} }

View File

@ -1103,7 +1103,7 @@ export class RawContextKey<T> extends ContextKeyDefinedExpr {
private readonly _defaultValue: T | undefined; private readonly _defaultValue: T | undefined;
constructor(key: string, defaultValue: T | undefined) { public constructor(key: string, defaultValue: T | undefined) {
super(key); super(key);
this._defaultValue = defaultValue; this._defaultValue = defaultValue;
} }

View File

@ -79,7 +79,7 @@ export default class EncryptionService {
}, },
}; };
constructor() { public constructor() {
// Note: 1 MB is very slow with Node and probably even worse on mobile. // Note: 1 MB is very slow with Node and probably even worse on mobile.
// //
// On mobile the time it takes to decrypt increases exponentially for some reason, so it's important // On mobile the time it takes to decrypt increases exponentially for some reason, so it's important
@ -117,23 +117,23 @@ export default class EncryptionService {
return this.defaultMasterKeyEncryptionMethod_; return this.defaultMasterKeyEncryptionMethod_;
} }
loadedMasterKeysCount() { public loadedMasterKeysCount() {
return Object.keys(this.decryptedMasterKeys_).length; return Object.keys(this.decryptedMasterKeys_).length;
} }
chunkSize() { public chunkSize() {
return this.chunkSize_; return this.chunkSize_;
} }
defaultEncryptionMethod() { public defaultEncryptionMethod() {
return this.defaultEncryptionMethod_; return this.defaultEncryptionMethod_;
} }
setActiveMasterKeyId(id: string) { public setActiveMasterKeyId(id: string) {
setActiveMasterKeyId(id); setActiveMasterKeyId(id);
} }
activeMasterKeyId() { public activeMasterKeyId() {
const id = getActiveMasterKeyId(); const id = getActiveMasterKeyId();
if (!id) { if (!id) {
const error: any = new Error('No master key is defined as active. Check this: Either one or more master keys exist but no password was provided for any of them. Or no master key exist. Or master keys and password exist, but none was set as active.'); const error: any = new Error('No master key is defined as active. Check this: Either one or more master keys exist but no password was provided for any of them. Or no master key exist. Or master keys and password exist, but none was set as active.');
@ -162,11 +162,11 @@ export default class EncryptionService {
if (makeActive) this.setActiveMasterKeyId(model.id); if (makeActive) this.setActiveMasterKeyId(model.id);
} }
unloadMasterKey(model: MasterKeyEntity) { public unloadMasterKey(model: MasterKeyEntity) {
delete this.decryptedMasterKeys_[model.id]; delete this.decryptedMasterKeys_[model.id];
} }
loadedMasterKey(id: string) { public loadedMasterKey(id: string) {
if (!this.decryptedMasterKeys_[id]) { if (!this.decryptedMasterKeys_[id]) {
const error: any = new Error(`Master key is not loaded: ${id}`); const error: any = new Error(`Master key is not loaded: ${id}`);
error.code = 'masterKeyNotLoaded'; error.code = 'masterKeyNotLoaded';
@ -176,22 +176,22 @@ export default class EncryptionService {
return this.decryptedMasterKeys_[id]; return this.decryptedMasterKeys_[id];
} }
loadedMasterKeyIds() { public loadedMasterKeyIds() {
return Object.keys(this.decryptedMasterKeys_); return Object.keys(this.decryptedMasterKeys_);
} }
fsDriver() { public fsDriver() {
if (!EncryptionService.fsDriver_) throw new Error('EncryptionService.fsDriver_ not set!'); if (!EncryptionService.fsDriver_) throw new Error('EncryptionService.fsDriver_ not set!');
return EncryptionService.fsDriver_; return EncryptionService.fsDriver_;
} }
sha256(string: string) { public sha256(string: string) {
const sjcl = shim.sjclModule; const sjcl = shim.sjclModule;
const bitArray = sjcl.hash.sha256.hash(string); const bitArray = sjcl.hash.sha256.hash(string);
return sjcl.codec.hex.fromBits(bitArray); return sjcl.codec.hex.fromBits(bitArray);
} }
async generateApiToken() { public async generateApiToken() {
return await this.randomHexString(64); return await this.randomHexString(64);
} }
@ -390,7 +390,7 @@ export default class EncryptionService {
throw new Error(`Unknown encryption method: ${method}`); throw new Error(`Unknown encryption method: ${method}`);
} }
async decrypt(method: EncryptionMethod, key: string, cipherText: string) { public async decrypt(method: EncryptionMethod, key: string, cipherText: string) {
if (!method) throw new Error('Encryption method is required'); if (!method) throw new Error('Encryption method is required');
if (!key) throw new Error('Encryption key is required'); if (!key) throw new Error('Encryption key is required');
@ -411,7 +411,7 @@ export default class EncryptionService {
} }
} }
async encryptAbstract_(source: any, destination: any, options: EncryptOptions = null) { private async encryptAbstract_(source: any, destination: any, options: EncryptOptions = null) {
options = Object.assign({}, { options = Object.assign({}, {
encryptionMethod: this.defaultEncryptionMethod(), encryptionMethod: this.defaultEncryptionMethod(),
}, options); }, options);
@ -447,7 +447,7 @@ export default class EncryptionService {
} }
} }
async decryptAbstract_(source: any, destination: any, options: EncryptOptions = null) { private async decryptAbstract_(source: any, destination: any, options: EncryptOptions = null) {
if (!options) options = {}; if (!options) options = {};
const header: any = await this.decodeHeaderSource_(source); const header: any = await this.decodeHeaderSource_(source);
@ -474,7 +474,7 @@ export default class EncryptionService {
} }
} }
stringReader_(string: string, sync = false) { private stringReader_(string: string, sync = false) {
const reader = { const reader = {
index: 0, index: 0,
read: function(size: number) { read: function(size: number) {
@ -487,7 +487,7 @@ export default class EncryptionService {
return reader; return reader;
} }
stringWriter_() { private stringWriter_() {
const output: any = { const output: any = {
data: [], data: [],
append: async function(data: any) { append: async function(data: any) {
@ -501,7 +501,7 @@ export default class EncryptionService {
return output; return output;
} }
async fileReader_(path: string, encoding: any) { private async fileReader_(path: string, encoding: any) {
const handle = await this.fsDriver().open(path, 'r'); const handle = await this.fsDriver().open(path, 'r');
const reader = { const reader = {
handle: handle, handle: handle,
@ -515,7 +515,7 @@ export default class EncryptionService {
return reader; return reader;
} }
async fileWriter_(path: string, encoding: any) { private async fileWriter_(path: string, encoding: any) {
return { return {
append: async (data: any) => { append: async (data: any) => {
return this.fsDriver().appendFile(path, data, encoding); return this.fsDriver().appendFile(path, data, encoding);
@ -538,7 +538,7 @@ export default class EncryptionService {
return destination.data.join(''); return destination.data.join('');
} }
async encryptFile(srcPath: string, destPath: string, options: EncryptOptions = null) { public async encryptFile(srcPath: string, destPath: string, options: EncryptOptions = null) {
let source = await this.fileReader_(srcPath, 'base64'); let source = await this.fileReader_(srcPath, 'base64');
let destination = await this.fileWriter_(destPath, 'ascii'); let destination = await this.fileWriter_(destPath, 'ascii');
@ -563,7 +563,7 @@ export default class EncryptionService {
await cleanUp(); await cleanUp();
} }
async decryptFile(srcPath: string, destPath: string, options: EncryptOptions = null) { public async decryptFile(srcPath: string, destPath: string, options: EncryptOptions = null) {
let source = await this.fileReader_(srcPath, 'ascii'); let source = await this.fileReader_(srcPath, 'ascii');
let destination = await this.fileWriter_(destPath, 'base64'); let destination = await this.fileWriter_(destPath, 'base64');
@ -588,13 +588,13 @@ export default class EncryptionService {
await cleanUp(); await cleanUp();
} }
headerTemplate(version: number) { public headerTemplate(version: number) {
const r = (this.headerTemplates_ as any)[version]; const r = (this.headerTemplates_ as any)[version];
if (!r) throw new Error(`Unknown header version: ${version}`); if (!r) throw new Error(`Unknown header version: ${version}`);
return r; return r;
} }
encodeHeader_(header: any) { public encodeHeader_(header: any) {
// Sanity check // Sanity check
if (header.masterKeyId.length !== 32) throw new Error(`Invalid master key ID size: ${header.masterKeyId}`); if (header.masterKeyId.length !== 32) throw new Error(`Invalid master key ID size: ${header.masterKeyId}`);
@ -605,12 +605,12 @@ export default class EncryptionService {
return `JED01${encryptionMetadata}`; return `JED01${encryptionMetadata}`;
} }
async decodeHeaderString(cipherText: any) { public async decodeHeaderString(cipherText: any) {
const source = this.stringReader_(cipherText); const source = this.stringReader_(cipherText);
return this.decodeHeaderSource_(source); return this.decodeHeaderSource_(source);
} }
async decodeHeaderSource_(source: any) { private async decodeHeaderSource_(source: any) {
const identifier = await source.read(5); const identifier = await source.read(5);
if (!isValidHeaderIdentifier(identifier)) throw new JoplinError(`Invalid encryption identifier. Data is not actually encrypted? ID was: ${identifier}`, 'invalidIdentifier'); if (!isValidHeaderIdentifier(identifier)) throw new JoplinError(`Invalid encryption identifier. Data is not actually encrypted? ID was: ${identifier}`, 'invalidIdentifier');
const mdSizeHex = await source.read(6); const mdSizeHex = await source.read(6);
@ -620,7 +620,7 @@ export default class EncryptionService {
return this.decodeHeaderBytes_(identifier + mdSizeHex + md); return this.decodeHeaderBytes_(identifier + mdSizeHex + md);
} }
decodeHeaderBytes_(headerHexaBytes: any) { public decodeHeaderBytes_(headerHexaBytes: any) {
const reader: any = this.stringReader_(headerHexaBytes, true); const reader: any = this.stringReader_(headerHexaBytes, true);
const identifier = reader.read(3); const identifier = reader.read(3);
const version = parseInt(reader.read(2), 16); const version = parseInt(reader.read(2), 16);
@ -652,18 +652,18 @@ export default class EncryptionService {
return output; return output;
} }
isValidEncryptionMethod(method: EncryptionMethod) { public isValidEncryptionMethod(method: EncryptionMethod) {
return [EncryptionMethod.SJCL, EncryptionMethod.SJCL1a, EncryptionMethod.SJCL2, EncryptionMethod.SJCL3, EncryptionMethod.SJCL4].indexOf(method) >= 0; return [EncryptionMethod.SJCL, EncryptionMethod.SJCL1a, EncryptionMethod.SJCL2, EncryptionMethod.SJCL3, EncryptionMethod.SJCL4].indexOf(method) >= 0;
} }
async itemIsEncrypted(item: any) { public async itemIsEncrypted(item: any) {
if (!item) throw new Error('No item'); if (!item) throw new Error('No item');
const ItemClass = BaseItem.itemClass(item); const ItemClass = BaseItem.itemClass(item);
if (!ItemClass.encryptionSupported()) return false; if (!ItemClass.encryptionSupported()) return false;
return item.encryption_applied && isValidHeaderIdentifier(item.encryption_cipher_text, true); return item.encryption_applied && isValidHeaderIdentifier(item.encryption_cipher_text, true);
} }
async fileIsEncrypted(path: string) { public async fileIsEncrypted(path: string) {
const handle = await this.fsDriver().open(path, 'r'); const handle = await this.fsDriver().open(path, 'r');
const headerIdentifier = await this.fsDriver().readFileChunk(handle, 5, 'ascii'); const headerIdentifier = await this.fsDriver().readFileChunk(handle, 5, 'ascii');
await this.fsDriver().close(handle); await this.fsDriver().close(handle);

View File

@ -7,32 +7,32 @@ export default class InteropService_Exporter_Base {
private metadata_: any = {}; private metadata_: any = {};
// @ts-ignore // @ts-ignore
async init(destDir: string, options: any = {}) {} public async init(destDir: string, options: any = {}) {}
// @ts-ignore // @ts-ignore
async prepareForProcessingItemType(itemType: number, itemsToExport: any[]) {} public async prepareForProcessingItemType(itemType: number, itemsToExport: any[]) {}
// @ts-ignore // @ts-ignore
async processItem(itemType: number, item: any) {} public async processItem(itemType: number, item: any) {}
// @ts-ignore // @ts-ignore
async processResource(resource: any, filePath: string) {} public async processResource(resource: any, filePath: string) {}
async close() {} public async close() {}
setMetadata(md: any) { public setMetadata(md: any) {
this.metadata_ = md; this.metadata_ = md;
} }
metadata() { public metadata() {
return this.metadata_; return this.metadata_;
} }
updateContext(context: any) { public updateContext(context: any) {
this.context_ = Object.assign({}, this.context_, context); this.context_ = Object.assign({}, this.context_, context);
} }
context() { public context() {
return this.context_; return this.context_;
} }
async temporaryDirectory_(createIt: boolean) { protected async temporaryDirectory_(createIt: boolean) {
const md5 = require('md5'); const md5 = require('md5');
const tempDir = `${Setting.value('tempDir')}/${md5(Math.random() + Date.now())}`; const tempDir = `${Setting.value('tempDir')}/${md5(Math.random() + Date.now())}`;
if (createIt) await require('fs-extra').mkdirp(tempDir); if (createIt) await require('fs-extra').mkdirp(tempDir);

View File

@ -7,12 +7,12 @@ export default class InteropService_Exporter_Custom extends InteropService_Expor
private customContext_: ExportContext; private customContext_: ExportContext;
private module_: Module = null; private module_: Module = null;
constructor(module: Module) { public constructor(module: Module) {
super(); super();
this.module_ = module; this.module_ = module;
} }
async init(destPath: string, options: ExportOptions) { public async init(destPath: string, options: ExportOptions) {
this.customContext_ = { this.customContext_ = {
destPath: destPath, destPath: destPath,
options: options, options: options,
@ -21,15 +21,15 @@ export default class InteropService_Exporter_Custom extends InteropService_Expor
return this.module_.onInit(this.customContext_); return this.module_.onInit(this.customContext_);
} }
async processItem(itemType: number, item: any) { public async processItem(itemType: number, item: any) {
return this.module_.onProcessItem(this.customContext_, itemType, item); return this.module_.onProcessItem(this.customContext_, itemType, item);
} }
async processResource(resource: any, filePath: string) { public async processResource(resource: any, filePath: string) {
return this.module_.onProcessResource(this.customContext_, resource, filePath); return this.module_.onProcessResource(this.customContext_, resource, filePath);
} }
async close() { public async close() {
return this.module_.onClose(this.customContext_); return this.module_.onClose(this.customContext_);
} }
} }

View File

@ -26,7 +26,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
private style_: any; private style_: any;
private packIntoSingleFile_: boolean = false; private packIntoSingleFile_: boolean = false;
async init(path: string, options: any = {}) { public async init(path: string, options: any = {}) {
this.customCss_ = options.customCss ? options.customCss : ''; this.customCss_ = options.customCss ? options.customCss : '';
if (this.metadata().target === 'file') { if (this.metadata().target === 'file') {
@ -48,7 +48,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
this.style_ = themeStyle(Setting.THEME_LIGHT); this.style_ = themeStyle(Setting.THEME_LIGHT);
} }
async makeDirPath_(item: any, pathPart: string = null) { private async makeDirPath_(item: any, pathPart: string = null) {
let output = ''; let output = '';
while (true) { while (true) {
if (item.type_ === BaseModel.TYPE_FOLDER) { if (item.type_ === BaseModel.TYPE_FOLDER) {
@ -64,7 +64,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
} }
} }
async processNoteResources_(item: any) { private async processNoteResources_(item: any) {
const target = this.metadata().target; const target = this.metadata().target;
const linkedResourceIds = await Note.linkedResourceIds(item.body); const linkedResourceIds = await Note.linkedResourceIds(item.body);
const relativePath = target === 'directory' ? rtrimSlashes(await this.makeDirPath_(item, '..')) : ''; const relativePath = target === 'directory' ? rtrimSlashes(await this.makeDirPath_(item, '..')) : '';
@ -85,7 +85,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
return newBody; return newBody;
} }
async processItem(_itemType: number, item: any) { public async processItem(_itemType: number, item: any) {
if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return; if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return;
let dirPath = ''; let dirPath = '';
@ -150,7 +150,7 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
} }
} }
async processResource(resource: any, filePath: string) { public async processResource(resource: any, filePath: string) {
const destResourcePath = `${this.resourceDir_}/${basename(filePath)}`; const destResourcePath = `${this.resourceDir_}/${basename(filePath)}`;
await shim.fsDriver().copy(filePath, destResourcePath); await shim.fsDriver().copy(filePath, destResourcePath);
this.resources_.push(resource); this.resources_.push(resource);

View File

@ -14,7 +14,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
private resourceDir_: string; private resourceDir_: string;
private createdDirs_: string[]; private createdDirs_: string[];
async init(destDir: string) { public async init(destDir: string) {
this.destDir_ = destDir; this.destDir_ = destDir;
this.resourceDir_ = destDir ? `${destDir}/_resources` : null; this.resourceDir_ = destDir ? `${destDir}/_resources` : null;
this.createdDirs_ = []; this.createdDirs_ = [];
@ -23,7 +23,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
await shim.fsDriver().mkdir(this.resourceDir_); await shim.fsDriver().mkdir(this.resourceDir_);
} }
async makeDirPath_(item: any, pathPart: string = null, findUniqueFilename: boolean = true) { private async makeDirPath_(item: any, pathPart: string = null, findUniqueFilename: boolean = true) {
let output = ''; let output = '';
while (true) { while (true) {
if (item.type_ === BaseModel.TYPE_FOLDER) { if (item.type_ === BaseModel.TYPE_FOLDER) {
@ -39,14 +39,14 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
} }
} }
async relaceLinkedItemIdsByRelativePaths_(item: any) { private async relaceLinkedItemIdsByRelativePaths_(item: any) {
const relativePathToRoot = await this.makeDirPath_(item, '..'); const relativePathToRoot = await this.makeDirPath_(item, '..');
const newBody = await this.replaceResourceIdsByRelativePaths_(item.body, relativePathToRoot); const newBody = await this.replaceResourceIdsByRelativePaths_(item.body, relativePathToRoot);
return await this.replaceNoteIdsByRelativePaths_(newBody, relativePathToRoot); return await this.replaceNoteIdsByRelativePaths_(newBody, relativePathToRoot);
} }
async replaceResourceIdsByRelativePaths_(noteBody: string, relativePathToRoot: string) { private async replaceResourceIdsByRelativePaths_(noteBody: string, relativePathToRoot: string) {
const linkedResourceIds = await Note.linkedResourceIds(noteBody); const linkedResourceIds = await Note.linkedResourceIds(noteBody);
const resourcePaths = this.context() && this.context().destResourcePaths ? this.context().destResourcePaths : {}; const resourcePaths = this.context() && this.context().destResourcePaths ? this.context().destResourcePaths : {};
@ -56,7 +56,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
return await this.replaceItemIdsByRelativePaths_(noteBody, linkedResourceIds, resourcePaths, createRelativePath); return await this.replaceItemIdsByRelativePaths_(noteBody, linkedResourceIds, resourcePaths, createRelativePath);
} }
async replaceNoteIdsByRelativePaths_(noteBody: string, relativePathToRoot: string) { private async replaceNoteIdsByRelativePaths_(noteBody: string, relativePathToRoot: string) {
const linkedNoteIds = await Note.linkedNoteIds(noteBody); const linkedNoteIds = await Note.linkedNoteIds(noteBody);
const notePaths = this.context() && this.context().notePaths ? this.context().notePaths : {}; const notePaths = this.context() && this.context().notePaths ? this.context().notePaths : {};
@ -66,7 +66,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
return await this.replaceItemIdsByRelativePaths_(noteBody, linkedNoteIds, notePaths, createRelativePath); return await this.replaceItemIdsByRelativePaths_(noteBody, linkedNoteIds, notePaths, createRelativePath);
} }
async replaceItemIdsByRelativePaths_(noteBody: string, linkedItemIds: string[], paths: any, fn_createRelativePath: Function) { private async replaceItemIdsByRelativePaths_(noteBody: string, linkedItemIds: string[], paths: any, fn_createRelativePath: Function) {
let newBody = noteBody; let newBody = noteBody;
for (let i = 0; i < linkedItemIds.length; i++) { for (let i = 0; i < linkedItemIds.length; i++) {
@ -78,7 +78,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
return newBody; return newBody;
} }
async prepareForProcessingItemType(itemType: number, itemsToExport: any[]) { public async prepareForProcessingItemType(itemType: number, itemsToExport: any[]) {
if (itemType === BaseModel.TYPE_NOTE) { if (itemType === BaseModel.TYPE_NOTE) {
// Create unique file path for the note // Create unique file path for the note
const context: any = { const context: any = {
@ -114,7 +114,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
return await Note.replaceResourceInternalToExternalLinks(await Note.serialize(modNote, ['body'])); return await Note.replaceResourceInternalToExternalLinks(await Note.serialize(modNote, ['body']));
} }
async processItem(_itemType: number, item: any) { public async processItem(_itemType: number, item: any) {
if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return; if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return;
if (item.type_ === BaseModel.TYPE_FOLDER) { if (item.type_ === BaseModel.TYPE_FOLDER) {
@ -150,7 +150,7 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
return fileName; return fileName;
} }
async processResource(resource: ResourceEntity, filePath: string) { public async processResource(resource: ResourceEntity, filePath: string) {
const context = this.context(); const context = this.context();
if (!context.destResourcePaths) context.destResourcePaths = {}; if (!context.destResourcePaths) context.destResourcePaths = {};
@ -163,5 +163,5 @@ export default class InteropService_Exporter_Md extends InteropService_Exporter_
this.updateContext(context); this.updateContext(context);
} }
async close() {} public async close() {}
} }

View File

@ -8,7 +8,7 @@ export default class InteropService_Exporter_Raw extends InteropService_Exporter
private destDir_: string; private destDir_: string;
private resourceDir_: string; private resourceDir_: string;
async init(destDir: string) { public async init(destDir: string) {
this.destDir_ = destDir; this.destDir_ = destDir;
this.resourceDir_ = destDir ? `${destDir}/resources` : null; this.resourceDir_ = destDir ? `${destDir}/resources` : null;
@ -16,17 +16,17 @@ export default class InteropService_Exporter_Raw extends InteropService_Exporter
await shim.fsDriver().mkdir(this.resourceDir_); await shim.fsDriver().mkdir(this.resourceDir_);
} }
async processItem(itemType: number, item: any) { public async processItem(itemType: number, item: any) {
const ItemClass = BaseItem.getClassByItemType(itemType); const ItemClass = BaseItem.getClassByItemType(itemType);
const serialized = await ItemClass.serialize(item); const serialized = await ItemClass.serialize(item);
const filePath = `${this.destDir_}/${ItemClass.systemPath(item)}`; const filePath = `${this.destDir_}/${ItemClass.systemPath(item)}`;
await shim.fsDriver().writeFile(filePath, serialized, 'utf-8'); await shim.fsDriver().writeFile(filePath, serialized, 'utf-8');
} }
async processResource(_resource: any, filePath: string) { public async processResource(_resource: any, filePath: string) {
const destResourcePath = `${this.resourceDir_}/${basename(filePath)}`; const destResourcePath = `${this.resourceDir_}/${basename(filePath)}`;
await shim.fsDriver().copy(filePath, destResourcePath); await shim.fsDriver().copy(filePath, destResourcePath);
} }
async close() {} public async close() {}
} }

Some files were not shown because too many files have changed in this diff Show More