You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-30 23:44:55 +02:00
All: Add support for application plugins (#3257)
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { replaceBetween } from './utils';
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
export default ({ getState, item, setState }) => {
|
||||
let { text } = getState();
|
||||
@ -36,7 +37,7 @@ export default ({ getState, item, setState }) => {
|
||||
}
|
||||
|
||||
setState({ text: newText }, () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
setState({ selection: newSelection });
|
||||
}, 300);
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { isStringWebLink, replaceBetween } from './utils';
|
||||
|
||||
export const writeUrlTextHere = 'https://example.com';
|
||||
export const writeTextHereString = 'Write some text here';
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
export default ({ getState, item, setState }) => {
|
||||
@ -31,7 +32,7 @@ export default ({ getState, item, setState }) => {
|
||||
};
|
||||
}
|
||||
setState({ text: newText }, () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
setState({ selection: newSelection });
|
||||
}, 25);
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { replaceBetween } from './utils';
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
export default ({ getState, item, setState }) => {
|
||||
const { text, selection } = getState();
|
||||
@ -20,7 +21,7 @@ export default ({ getState, item, setState }) => {
|
||||
},
|
||||
};
|
||||
setState({ text: newText }, () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
setState({ ...extra });
|
||||
}, 25);
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { replaceBetween } from './utils';
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
export default ({ getState, item, setState }) => {
|
||||
const { text, selection } = getState();
|
||||
@ -48,7 +49,7 @@ export default ({ getState, item, setState }) => {
|
||||
},
|
||||
};
|
||||
setState({ text: newText }, () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
setState({ ...extra });
|
||||
}, 25);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import shim from 'lib/shim';
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const pluginAssets = require('./pluginAssets/index');
|
||||
const KvStore = require('lib/services/KvStore.js');
|
||||
|
||||
|
@ -126,7 +126,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097582
|
||||
versionName "1.2.5"
|
||||
versionName "1.3.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
@ -357,7 +357,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
MARKETING_VERSION = 10.2.0;
|
||||
MARKETING_VERSION = 10.3.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@ -393,7 +393,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
MARKETING_VERSION = 10.2.0;
|
||||
MARKETING_VERSION = 10.3.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1,3 +1,5 @@
|
||||
import shim from 'lib/shim';
|
||||
|
||||
export interface QueueItemAction {
|
||||
(): void,
|
||||
}
|
||||
@ -47,10 +49,10 @@ export default class AsyncActionQueue {
|
||||
|
||||
if (this.scheduleProcessingIID_) {
|
||||
if (this.intervalType_ === IntervalType.Fixed) return;
|
||||
clearTimeout(this.scheduleProcessingIID_);
|
||||
shim.clearTimeout(this.scheduleProcessingIID_);
|
||||
}
|
||||
|
||||
this.scheduleProcessingIID_ = setTimeout(() => {
|
||||
this.scheduleProcessingIID_ = shim.setTimeout(() => {
|
||||
this.scheduleProcessingIID_ = null;
|
||||
this.processQueue();
|
||||
}, interval);
|
||||
@ -77,7 +79,7 @@ export default class AsyncActionQueue {
|
||||
|
||||
async reset() {
|
||||
if (this.scheduleProcessingIID_) {
|
||||
clearTimeout(this.scheduleProcessingIID_);
|
||||
shim.clearTimeout(this.scheduleProcessingIID_);
|
||||
this.scheduleProcessingIID_ = null;
|
||||
}
|
||||
|
||||
@ -97,11 +99,11 @@ export default class AsyncActionQueue {
|
||||
this.scheduleProcessing(1);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
if (this.processing_) return;
|
||||
|
||||
if (!this.queue_.length) {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
@ -1,5 +1,13 @@
|
||||
import Setting from 'lib/models/Setting';
|
||||
import Logger, { TargetType } from 'lib/Logger';
|
||||
import shim from 'lib/shim';
|
||||
import BaseService from 'lib/services/BaseService';
|
||||
import reducer from 'lib/reducer';
|
||||
import KeychainServiceDriver from 'lib/services/keychain/KeychainServiceDriver.node';
|
||||
import { _, setLocale } from 'lib/locale';
|
||||
|
||||
const { createStore, applyMiddleware } = require('redux');
|
||||
const { reducer, defaultState, stateUtils } = require('lib/reducer.js');
|
||||
const { defaultState, stateUtils } = require('lib/reducer');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
@ -8,14 +16,10 @@ const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const reduxSharedMiddleware = require('lib/components/shared/reduxSharedMiddleware');
|
||||
const os = require('os');
|
||||
const fs = require('fs-extra');
|
||||
@ -37,33 +41,44 @@ const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const RevisionService = require('lib/services/RevisionService');
|
||||
const ResourceService = require('lib/services/RevisionService');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const BaseService = require('lib/services/BaseService');
|
||||
const { loadKeychainServiceAndSettings } = require('lib/services/SettingUtils');
|
||||
const KeychainServiceDriver = require('lib/services/keychain/KeychainServiceDriver.node').default;
|
||||
const KvStore = require('lib/services/KvStore');
|
||||
const MigrationService = require('lib/services/MigrationService');
|
||||
const { toSystemSlashes } = require('lib/path-utils.js');
|
||||
const { setAutoFreeze } = require('immer');
|
||||
|
||||
// const ntpClient = require('lib/vendor/ntp-client');
|
||||
// ntpClient.dgram = require('dgram');
|
||||
|
||||
class BaseApplication {
|
||||
export default class BaseApplication {
|
||||
|
||||
private logger_:Logger;
|
||||
private dbLogger_:Logger;
|
||||
private eventEmitter_:any;
|
||||
private scheduleAutoAddResourcesIID_:any = null;
|
||||
private database_:any = null;
|
||||
|
||||
protected showStackTraces_:boolean = false;
|
||||
protected showPromptString_:boolean = false;
|
||||
|
||||
// Note: this is basically a cache of state.selectedFolderId. It should *only*
|
||||
// be derived from the state and not set directly since that would make the
|
||||
// state and UI out of sync.
|
||||
private currentFolder_:any = null;
|
||||
|
||||
protected store_:any = null;
|
||||
|
||||
constructor() {
|
||||
this.logger_ = new Logger();
|
||||
this.dbLogger_ = new Logger();
|
||||
this.eventEmitter_ = new EventEmitter();
|
||||
|
||||
// Note: this is basically a cache of state.selectedFolderId. It should *only*
|
||||
// be derived from the state and not set directly since that would make the
|
||||
// state and UI out of sync.
|
||||
this.currentFolder_ = null;
|
||||
|
||||
this.decryptionWorker_resourceMetadataButNotBlobDecrypted = this.decryptionWorker_resourceMetadataButNotBlobDecrypted.bind(this);
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
if (this.scheduleAutoAddResourcesIID_) {
|
||||
clearTimeout(this.scheduleAutoAddResourcesIID_);
|
||||
shim.clearTimeout(this.scheduleAutoAddResourcesIID_);
|
||||
this.scheduleAutoAddResourcesIID_ = null;
|
||||
}
|
||||
await ResourceFetcher.instance().destroy();
|
||||
@ -98,7 +113,7 @@ class BaseApplication {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
store() {
|
||||
public store() {
|
||||
return this.store_;
|
||||
}
|
||||
|
||||
@ -115,7 +130,7 @@ class BaseApplication {
|
||||
this.switchCurrentFolder(newFolder);
|
||||
}
|
||||
|
||||
switchCurrentFolder(folder) {
|
||||
switchCurrentFolder(folder:any) {
|
||||
if (!this.hasGui()) {
|
||||
this.currentFolder_ = Object.assign({}, folder);
|
||||
Setting.setValue('activeFolderId', folder ? folder.id : '');
|
||||
@ -129,8 +144,8 @@ class BaseApplication {
|
||||
|
||||
// Handles the initial flags passed to main script and
|
||||
// returns the remaining args.
|
||||
async handleStartFlags_(argv, setDefaults = true) {
|
||||
const matched = {};
|
||||
async handleStartFlags_(argv:string[], setDefaults:boolean = true) {
|
||||
const matched:any = {};
|
||||
argv = argv.slice(0);
|
||||
argv.splice(0, 2); // First arguments are the node executable, and the node JS file
|
||||
|
||||
@ -209,6 +224,12 @@ class BaseApplication {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--dev-plugins') {
|
||||
Setting.setConstant('startupDevPlugins', nextArg.split(',').map(p => p.trim()));
|
||||
argv.splice(0, 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.indexOf('--remote-debugging-port=') === 0) {
|
||||
// Electron-specific flag used for debugging - ignore it. Electron expects this flag in '--x=y' form, a single string.
|
||||
argv.splice(0, 1);
|
||||
@ -233,6 +254,7 @@ class BaseApplication {
|
||||
if (setDefaults) {
|
||||
if (!matched.logLevel) matched.logLevel = Logger.LEVEL_INFO;
|
||||
if (!matched.env) matched.env = 'prod';
|
||||
if (!matched.devPlugins) matched.devPlugins = [];
|
||||
}
|
||||
|
||||
return {
|
||||
@ -241,7 +263,7 @@ class BaseApplication {
|
||||
};
|
||||
}
|
||||
|
||||
on(eventName, callback) {
|
||||
on(eventName:string, callback:Function) {
|
||||
return this.eventEmitter_.on(eventName, callback);
|
||||
}
|
||||
|
||||
@ -250,7 +272,7 @@ class BaseApplication {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
async refreshNotes(state, useSelectedNoteId = false, noteHash = '') {
|
||||
async refreshNotes(state:any, useSelectedNoteId:boolean = false, noteHash:string = '') {
|
||||
let parentType = state.notesParentType;
|
||||
let parentId = null;
|
||||
|
||||
@ -346,7 +368,7 @@ class BaseApplication {
|
||||
}
|
||||
}
|
||||
|
||||
resourceFetcher_downloadComplete(event) {
|
||||
resourceFetcher_downloadComplete(event:any) {
|
||||
if (event.encrypted) {
|
||||
DecryptionWorker.instance().scheduleStart();
|
||||
}
|
||||
@ -356,7 +378,7 @@ class BaseApplication {
|
||||
ResourceFetcher.instance().scheduleAutoAddResources();
|
||||
}
|
||||
|
||||
reducerActionToString(action) {
|
||||
reducerActionToString(action:any) {
|
||||
const o = [action.type];
|
||||
if ('id' in action) o.push(action.id);
|
||||
if ('noteId' in action) o.push(action.noteId);
|
||||
@ -377,15 +399,15 @@ class BaseApplication {
|
||||
}
|
||||
|
||||
generalMiddlewareFn() {
|
||||
const middleware = store => next => action => {
|
||||
const middleware = (store:any) => (next:any) => (action:any) => {
|
||||
return this.generalMiddleware(store, next, action);
|
||||
};
|
||||
|
||||
return middleware;
|
||||
}
|
||||
|
||||
async applySettingsSideEffects(action = null) {
|
||||
const sideEffects = {
|
||||
async applySettingsSideEffects(action:any = null) {
|
||||
const sideEffects:any = {
|
||||
'dateFormat': async () => {
|
||||
time.setLocale(Setting.value('locale'));
|
||||
time.setDateFormat(Setting.value('dateFormat'));
|
||||
@ -438,13 +460,13 @@ class BaseApplication {
|
||||
}
|
||||
}
|
||||
|
||||
async generalMiddleware(store, next, action) {
|
||||
async generalMiddleware(store:any, next:any, action:any) {
|
||||
// this.logger().debug('Reducer action', this.reducerActionToString(action));
|
||||
|
||||
const result = next(action);
|
||||
const newState = store.getState();
|
||||
let refreshNotes = false;
|
||||
let refreshFolders = false;
|
||||
let refreshFolders:boolean | string = false;
|
||||
// let refreshTags = false;
|
||||
let refreshNotesUseSelectedNoteId = false;
|
||||
let refreshNotesHash = '';
|
||||
@ -564,11 +586,11 @@ class BaseApplication {
|
||||
return result;
|
||||
}
|
||||
|
||||
dispatch(action) {
|
||||
dispatch(action:any) {
|
||||
if (this.store()) return this.store().dispatch(action);
|
||||
}
|
||||
|
||||
reducer(state = defaultState, action) {
|
||||
reducer(state:any = defaultState, action:any) {
|
||||
return reducer(state, action);
|
||||
}
|
||||
|
||||
@ -592,7 +614,7 @@ class BaseApplication {
|
||||
ResourceFetcher.instance().dispatch = function() {};
|
||||
}
|
||||
|
||||
async readFlagsFromFile(flagPath) {
|
||||
async readFlagsFromFile(flagPath:string) {
|
||||
if (!fs.existsSync(flagPath)) return {};
|
||||
let flagContent = fs.readFileSync(flagPath, 'utf8');
|
||||
if (!flagContent) return {};
|
||||
@ -608,7 +630,7 @@ class BaseApplication {
|
||||
return flags.matched;
|
||||
}
|
||||
|
||||
determineProfileDir(initArgs) {
|
||||
determineProfileDir(initArgs:any) {
|
||||
let output = '';
|
||||
|
||||
if (initArgs.profileDir) {
|
||||
@ -622,7 +644,7 @@ class BaseApplication {
|
||||
return toSystemSlashes(output, 'linux');
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
async start(argv:string[]):Promise<any> {
|
||||
const startFlags = await this.handleStartFlags_(argv);
|
||||
|
||||
argv = startFlags.argv;
|
||||
@ -633,6 +655,9 @@ class BaseApplication {
|
||||
if (Setting.value('appId').indexOf('-desktop') >= 0) appName += '-desktop';
|
||||
Setting.setConstant('appName', appName);
|
||||
|
||||
// https://immerjs.github.io/immer/docs/freezing
|
||||
setAutoFreeze(initArgs.env === 'dev');
|
||||
|
||||
const profileDir = this.determineProfileDir(initArgs);
|
||||
const resourceDirName = 'resources';
|
||||
const resourceDir = `${profileDir}/${resourceDirName}`;
|
||||
@ -644,6 +669,7 @@ class BaseApplication {
|
||||
Setting.setConstant('resourceDirName', resourceDirName);
|
||||
Setting.setConstant('resourceDir', resourceDir);
|
||||
Setting.setConstant('tempDir', tempDir);
|
||||
Setting.setConstant('pluginDir', `${profileDir}/plugins`);
|
||||
|
||||
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
||||
SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||
@ -671,7 +697,7 @@ class BaseApplication {
|
||||
const extraFlags = await this.readFlagsFromFile(`${profileDir}/flags.txt`);
|
||||
initArgs = Object.assign(initArgs, extraFlags);
|
||||
|
||||
this.logger_.addTarget('file', { path: `${profileDir}/log.txt` });
|
||||
this.logger_.addTarget(TargetType.File, { path: `${profileDir}/log.txt` });
|
||||
this.logger_.setLevel(initArgs.logLevel);
|
||||
|
||||
reg.setLogger(this.logger_);
|
||||
@ -680,12 +706,12 @@ class BaseApplication {
|
||||
BaseService.logger_ = this.logger_;
|
||||
// require('lib/ntpDate').setLogger(reg.logger());
|
||||
|
||||
this.dbLogger_.addTarget('file', { path: `${profileDir}/log-database.txt` });
|
||||
this.dbLogger_.addTarget(TargetType.File, { path: `${profileDir}/log-database.txt` });
|
||||
this.dbLogger_.setLevel(initArgs.logLevel);
|
||||
|
||||
if (Setting.value('env') === 'dev' && Setting.value('appType') === 'desktop') {
|
||||
// this.logger_.addTarget('console', { level: Logger.LEVEL_DEBUG });
|
||||
this.dbLogger_.addTarget('console', { level: Logger.LEVEL_WARN });
|
||||
if (Setting.value('appType') === 'desktop') {
|
||||
this.logger_.addTarget(TargetType.Console, { level: Logger.LEVEL_WARN });
|
||||
this.dbLogger_.addTarget(TargetType.Console, { level: Logger.LEVEL_WARN });
|
||||
}
|
||||
|
||||
if (Setting.value('env') === 'dev') {
|
||||
@ -772,7 +798,7 @@ class BaseApplication {
|
||||
if (!Setting.value('api.token')) {
|
||||
EncryptionService.instance()
|
||||
.randomHexString(64)
|
||||
.then(token => {
|
||||
.then((token:string) => {
|
||||
Setting.setValue('api.token', token);
|
||||
});
|
||||
}
|
||||
@ -810,8 +836,7 @@ class BaseApplication {
|
||||
Setting.setValue('activeFolderId', currentFolder ? currentFolder.id : '');
|
||||
|
||||
await MigrationService.instance().run();
|
||||
|
||||
return argv;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { BaseApplication };
|
@ -1,5 +1,5 @@
|
||||
const { Database } = require('lib/database.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
const EncryptionService = require('lib/services/EncryptionService.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class BaseSyncTarget {
|
||||
constructor(db, options = null) {
|
||||
@ -87,13 +88,13 @@ class BaseSyncTarget {
|
||||
if (this.initState_ == 'started') {
|
||||
// Synchronizer is already being initialized, so wait here till it's done.
|
||||
return new Promise((resolve, reject) => {
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
if (this.initState_ == 'ready') {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
resolve(this.synchronizer_);
|
||||
}
|
||||
if (this.initState_ == 'error') {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
reject(new Error('Could not initialise synchroniser'));
|
||||
}
|
||||
}, 1000);
|
||||
|
@ -1,9 +1,9 @@
|
||||
const urlParser = require('url');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { randomClipperPort, startPort } = require('lib/randomClipperPort');
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
const Api = require('lib/services/rest/Api');
|
||||
const Api = require('lib/services/rest/Api').default;
|
||||
const ApiResponse = require('lib/services/rest/ApiResponse');
|
||||
const multiparty = require('multiparty');
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { time } = require('lib/time-utils');
|
||||
const EventDispatcher = require('lib/EventDispatcher');
|
||||
|
@ -1,5 +1,5 @@
|
||||
const TurndownService = require('joplin-turndown');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
|
||||
class HtmlToMd {
|
||||
parse(html, options = {}) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
import shim from 'lib/shim';
|
||||
import { _ } from 'lib/locale';
|
||||
const Logger = require('lib/Logger').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { rtrimSlashes } = require('lib/path-utils.js');
|
||||
const base64 = require('base-64');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
interface JoplinServerApiOptions {
|
||||
username: Function,
|
||||
|
@ -1,22 +1,52 @@
|
||||
const moment = require('moment');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { FsDriverDummy } = require('lib/fs-driver-dummy.js');
|
||||
|
||||
export enum TargetType {
|
||||
Database = 'database',
|
||||
File = 'file',
|
||||
Console = 'console',
|
||||
}
|
||||
|
||||
enum LogLevel {
|
||||
None = 0,
|
||||
Error = 10,
|
||||
Warn = 20,
|
||||
Info = 30,
|
||||
Debug = 40,
|
||||
}
|
||||
|
||||
interface Target {
|
||||
type: TargetType,
|
||||
level?: LogLevel,
|
||||
database?: any,
|
||||
console?: any,
|
||||
prefix?: string,
|
||||
path?: string,
|
||||
source?: string,
|
||||
}
|
||||
|
||||
class Logger {
|
||||
constructor() {
|
||||
this.targets_ = [];
|
||||
this.level_ = Logger.LEVEL_INFO;
|
||||
this.fileAppendQueue_ = [];
|
||||
this.lastDbCleanup_ = time.unixMs();
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
public static LEVEL_NONE = LogLevel.None;
|
||||
public static LEVEL_ERROR = LogLevel.Error;
|
||||
public static LEVEL_WARN = LogLevel.Warn;
|
||||
public static LEVEL_INFO = LogLevel.Info;
|
||||
public static LEVEL_DEBUG = LogLevel.Debug;
|
||||
|
||||
public static fsDriver_:any = null;
|
||||
|
||||
private targets_:Target[] = [];
|
||||
private level_:LogLevel = LogLevel.Info;
|
||||
private lastDbCleanup_:number = time.unixMs();
|
||||
|
||||
static fsDriver() {
|
||||
if (!Logger.fsDriver_) Logger.fsDriver_ = new FsDriverDummy();
|
||||
return Logger.fsDriver_;
|
||||
}
|
||||
|
||||
setLevel(level) {
|
||||
setLevel(level:LogLevel) {
|
||||
this.level_ = level;
|
||||
}
|
||||
|
||||
@ -28,25 +58,22 @@ class Logger {
|
||||
return this.targets_;
|
||||
}
|
||||
|
||||
clearTargets() {
|
||||
this.targets_.clear();
|
||||
}
|
||||
|
||||
addTarget(type, options = null) {
|
||||
addTarget(type:TargetType, options:any = null) {
|
||||
const target = { type: type };
|
||||
for (const n in options) {
|
||||
if (!options.hasOwnProperty(n)) continue;
|
||||
target[n] = options[n];
|
||||
(target as any)[n] = options[n];
|
||||
}
|
||||
|
||||
this.targets_.push(target);
|
||||
}
|
||||
|
||||
objectToString(object) {
|
||||
objectToString(object:any) {
|
||||
let output = '';
|
||||
|
||||
if (typeof object === 'object') {
|
||||
if (object instanceof Error) {
|
||||
object = object as any;
|
||||
output = object.toString();
|
||||
if (object.code) output += `\nCode: ${object.code}`;
|
||||
if (object.headers) output += `\nHeader: ${JSON.stringify(object.headers)}`;
|
||||
@ -62,7 +89,7 @@ class Logger {
|
||||
return output;
|
||||
}
|
||||
|
||||
objectsToString(...object) {
|
||||
objectsToString(...object:any[]) {
|
||||
const output = [];
|
||||
for (let i = 0; i < object.length; i++) {
|
||||
output.push(`"${this.objectToString(object[i])}"`);
|
||||
@ -84,9 +111,9 @@ class Logger {
|
||||
}
|
||||
|
||||
// Only for database at the moment
|
||||
async lastEntries(limit = 100, options = null) {
|
||||
async lastEntries(limit:number = 100, options:any = null) {
|
||||
if (options === null) options = {};
|
||||
if (!options.levels) options.levels = [Logger.LEVEL_DEBUG, Logger.LEVEL_INFO, Logger.LEVEL_WARN, Logger.LEVEL_ERROR];
|
||||
if (!options.levels) options.levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
|
||||
if (!options.levels.length) return [];
|
||||
|
||||
for (let i = 0; i < this.targets_.length; i++) {
|
||||
@ -100,12 +127,12 @@ class Logger {
|
||||
return [];
|
||||
}
|
||||
|
||||
targetLevel(target) {
|
||||
targetLevel(target:Target) {
|
||||
if ('level' in target) return target.level;
|
||||
return this.level();
|
||||
}
|
||||
|
||||
log(level, ...object) {
|
||||
log(level:LogLevel, ...object:any[]) {
|
||||
if (!this.targets_.length) return;
|
||||
|
||||
const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||
@ -118,11 +145,15 @@ class Logger {
|
||||
|
||||
if (target.type == 'console') {
|
||||
let fn = 'log';
|
||||
if (level == Logger.LEVEL_ERROR) fn = 'error';
|
||||
if (level == Logger.LEVEL_WARN) fn = 'warn';
|
||||
if (level == Logger.LEVEL_INFO) fn = 'info';
|
||||
if (level == LogLevel.Error) fn = 'error';
|
||||
if (level == LogLevel.Warn) fn = 'warn';
|
||||
if (level == LogLevel.Info) fn = 'info';
|
||||
const consoleObj = target.console ? target.console : console;
|
||||
const items = [moment().format('HH:mm:ss')].concat(object);
|
||||
let items = [moment().format('HH:mm:ss')];
|
||||
if (target.prefix) {
|
||||
items.push(target.prefix);
|
||||
}
|
||||
items = items.concat(...object);
|
||||
consoleObj[fn](...items);
|
||||
} else if (target.type == 'file') {
|
||||
const serializedObject = this.objectsToString(...object);
|
||||
@ -156,55 +187,41 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
error(...object) {
|
||||
return this.log(Logger.LEVEL_ERROR, ...object);
|
||||
error(...object:any[]) {
|
||||
return this.log(LogLevel.Error, ...object);
|
||||
}
|
||||
warn(...object) {
|
||||
return this.log(Logger.LEVEL_WARN, ...object);
|
||||
warn(...object:any[]) {
|
||||
return this.log(LogLevel.Warn, ...object);
|
||||
}
|
||||
info(...object) {
|
||||
return this.log(Logger.LEVEL_INFO, ...object);
|
||||
info(...object:any[]) {
|
||||
return this.log(LogLevel.Info, ...object);
|
||||
}
|
||||
debug(...object) {
|
||||
return this.log(Logger.LEVEL_DEBUG, ...object);
|
||||
debug(...object:any[]) {
|
||||
return this.log(LogLevel.Debug, ...object);
|
||||
}
|
||||
|
||||
static levelStringToId(s) {
|
||||
if (s == 'none') return Logger.LEVEL_NONE;
|
||||
if (s == 'error') return Logger.LEVEL_ERROR;
|
||||
if (s == 'warn') return Logger.LEVEL_WARN;
|
||||
if (s == 'info') return Logger.LEVEL_INFO;
|
||||
if (s == 'debug') return Logger.LEVEL_DEBUG;
|
||||
throw new Error(_('Unknown log level: %s', s));
|
||||
static levelStringToId(s:string) {
|
||||
if (s == 'none') return LogLevel.None;
|
||||
if (s == 'error') return LogLevel.Error;
|
||||
if (s == 'warn') return LogLevel.Warn;
|
||||
if (s == 'info') return LogLevel.Info;
|
||||
if (s == 'debug') return LogLevel.Debug;
|
||||
throw new Error(`Unknown log level: ${s}`);
|
||||
}
|
||||
|
||||
static levelIdToString(id) {
|
||||
if (id == Logger.LEVEL_NONE) return 'none';
|
||||
if (id == Logger.LEVEL_ERROR) return 'error';
|
||||
if (id == Logger.LEVEL_WARN) return 'warn';
|
||||
if (id == Logger.LEVEL_INFO) return 'info';
|
||||
if (id == Logger.LEVEL_DEBUG) return 'debug';
|
||||
throw new Error(_('Unknown level ID: %s', id));
|
||||
static levelIdToString(id:LogLevel) {
|
||||
if (id == LogLevel.None) return 'none';
|
||||
if (id == LogLevel.Error) return 'error';
|
||||
if (id == LogLevel.Warn) return 'warn';
|
||||
if (id == LogLevel.Info) return 'info';
|
||||
if (id == LogLevel.Debug) return 'debug';
|
||||
throw new Error(`Unknown level ID: ${id}`);
|
||||
}
|
||||
|
||||
static levelIds() {
|
||||
return [Logger.LEVEL_NONE, Logger.LEVEL_ERROR, Logger.LEVEL_WARN, Logger.LEVEL_INFO, Logger.LEVEL_DEBUG];
|
||||
return [LogLevel.None, LogLevel.Error, LogLevel.Warn, LogLevel.Info, LogLevel.Debug];
|
||||
}
|
||||
|
||||
static levelEnum() {
|
||||
const output = {};
|
||||
const ids = this.levelIds();
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
output[ids[i]] = this.levelIdToString(ids[i]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LEVEL_NONE = 0;
|
||||
Logger.LEVEL_ERROR = 10;
|
||||
Logger.LEVEL_WARN = 20;
|
||||
Logger.LEVEL_INFO = 30;
|
||||
Logger.LEVEL_DEBUG = 40;
|
||||
|
||||
module.exports = { Logger };
|
||||
export default Logger;
|
@ -1,8 +1,8 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const { FileApiDriverAmazonS3 } = require('lib/file-api-driver-amazon-s3.js');
|
||||
const S3 = require('aws-sdk/clients/s3');
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const DropboxApi = require('lib/DropboxApi');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { parameters } = require('lib/parameters.js');
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const { FileApiDriverDropbox } = require('lib/file-api-driver-dropbox.js');
|
||||
|
||||
class SyncTargetDropbox extends BaseSyncTarget {
|
||||
|
@ -1,9 +1,9 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
|
||||
class SyncTargetFilesystem extends BaseSyncTarget {
|
||||
static id() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { FileApiDriverMemory } = require('lib/file-api-driver-memory.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
|
||||
class SyncTargetMemory extends BaseSyncTarget {
|
||||
static id() {
|
||||
|
@ -2,9 +2,9 @@
|
||||
// thus all the calls to SyncTargetWebDAV to avoid duplicate code.
|
||||
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV');
|
||||
const JoplinServerApi = require('lib/JoplinServerApi.js').default;
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { OneDriveApi } = require('lib/onedrive-api.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { parameters } = require('lib/parameters.js');
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const { FileApiDriverOneDrive } = require('lib/file-api-driver-onedrive.js');
|
||||
|
||||
class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
|
@ -1,5 +1,5 @@
|
||||
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { parameters } = require('lib/parameters.js');
|
||||
|
||||
class SyncTargetOneDriveDev extends SyncTargetOneDrive {
|
||||
|
@ -1,8 +1,8 @@
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { FileApi } = require('lib/file-api.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const WebDavApi = require('lib/WebDavApi');
|
||||
const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav');
|
||||
|
||||
|
@ -1,40 +1,62 @@
|
||||
import Logger from './Logger';
|
||||
import LockHandler, { LockType } from 'lib/services/synchronizer/LockHandler';
|
||||
import Setting from 'lib/models/Setting';
|
||||
import shim from 'lib/shim';
|
||||
import MigrationHandler from 'lib/services/synchronizer/MigrationHandler';
|
||||
import eventManager from 'lib/eventManager';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const ResourceLocalState = require('lib/models/ResourceLocalState.js');
|
||||
const MasterKey = require('lib/models/MasterKey.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
// const { filename, fileExtension } = require('lib/path-utils');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const TaskQueue = require('lib/TaskQueue');
|
||||
const LockHandler = require('lib/services/synchronizer/LockHandler').default;
|
||||
const MigrationHandler = require('lib/services/synchronizer/MigrationHandler').default;
|
||||
const { Dirnames } = require('lib/services/synchronizer/utils/types');
|
||||
|
||||
class Synchronizer {
|
||||
constructor(db, api, appType) {
|
||||
this.state_ = 'idle';
|
||||
interface RemoteItem {
|
||||
id: string,
|
||||
path?: string,
|
||||
type_?: number,
|
||||
}
|
||||
|
||||
export default class Synchronizer {
|
||||
|
||||
private db_:any;
|
||||
private api_:any;
|
||||
private appType_:string;
|
||||
private logger_:Logger = new Logger();
|
||||
private state_:string = 'idle';
|
||||
private cancelling_:boolean = false;
|
||||
private maxResourceSize_:number = null;
|
||||
private downloadQueue_:any = null;
|
||||
private clientId_:string;
|
||||
private lockHandler_:LockHandler;
|
||||
private migrationHandler_:MigrationHandler;
|
||||
private encryptionService_:any = null;
|
||||
private syncTargetIsLocked_:boolean = false;
|
||||
|
||||
// Debug flags are used to test certain hard-to-test conditions
|
||||
// such as cancelling in the middle of a loop.
|
||||
public testingHooks_:string[] = [];
|
||||
|
||||
private onProgress_:Function;
|
||||
private progressReport_:any = {};
|
||||
|
||||
public dispatch:Function;
|
||||
|
||||
constructor(db:any, api:any, appType:string) {
|
||||
this.db_ = db;
|
||||
this.api_ = api;
|
||||
this.logger_ = new Logger();
|
||||
this.appType_ = appType;
|
||||
this.cancelling_ = false;
|
||||
this.maxResourceSize_ = null;
|
||||
this.downloadQueue_ = null;
|
||||
this.clientId_ = Setting.value('clientId');
|
||||
|
||||
// Debug flags are used to test certain hard-to-test conditions
|
||||
// such as cancelling in the middle of a loop.
|
||||
this.testingHooks_ = [];
|
||||
|
||||
this.onProgress_ = function() {};
|
||||
this.progressReport_ = {};
|
||||
|
||||
@ -57,7 +79,7 @@ class Synchronizer {
|
||||
return this.clientId_;
|
||||
}
|
||||
|
||||
setLogger(l) {
|
||||
setLogger(l:Logger) {
|
||||
this.logger_ = l;
|
||||
}
|
||||
|
||||
@ -82,7 +104,7 @@ class Synchronizer {
|
||||
return this.appType_ === 'mobile' ? 100 * 1000 * 1000 : Infinity;
|
||||
}
|
||||
|
||||
setEncryptionService(v) {
|
||||
setEncryptionService(v:any) {
|
||||
this.encryptionService_ = v;
|
||||
}
|
||||
|
||||
@ -99,7 +121,7 @@ class Synchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
static reportToLines(report) {
|
||||
static reportToLines(report:any) {
|
||||
const lines = [];
|
||||
if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal));
|
||||
if (report.updateLocal) lines.push(_('Updated local items: %d.', report.updateLocal));
|
||||
@ -108,7 +130,6 @@ class Synchronizer {
|
||||
if (report.deleteLocal) lines.push(_('Deleted local items: %d.', report.deleteLocal));
|
||||
if (report.deleteRemote) lines.push(_('Deleted remote items: %d.', report.deleteRemote));
|
||||
if (report.fetchingTotal && report.fetchingProcessed) lines.push(_('Fetched items: %d/%d.', report.fetchingProcessed, report.fetchingTotal));
|
||||
// if (!report.completedTime && report.state) lines.push(_('State: %s.', Synchronizer.stateToLabel(report.state)));
|
||||
if (report.cancelling && !report.completedTime) lines.push(_('Cancelling...'));
|
||||
if (report.completedTime) lines.push(_('Completed: %s', time.formatMsToLocal(report.completedTime)));
|
||||
if (report.errors && report.errors.length) lines.push(_('Last error: %s', report.errors[report.errors.length - 1].toString().substr(0, 500)));
|
||||
@ -116,7 +137,7 @@ class Synchronizer {
|
||||
return lines;
|
||||
}
|
||||
|
||||
logSyncOperation(action, local = null, remote = null, message = null, actionCount = 1) {
|
||||
logSyncOperation(action:any, local:any = null, remote:RemoteItem = null, message:string = null, actionCount:number = 1) {
|
||||
const line = ['Sync'];
|
||||
line.push(action);
|
||||
if (message) line.push(message);
|
||||
@ -145,10 +166,16 @@ class Synchronizer {
|
||||
this.progressReport_.state = this.state();
|
||||
this.onProgress_(this.progressReport_);
|
||||
|
||||
this.dispatch({ type: 'SYNC_REPORT_UPDATE', report: Object.assign({}, this.progressReport_) });
|
||||
// Make sure we only send a **copy** of the report since it
|
||||
// is mutated within this class. Should probably use a lib
|
||||
// for this but for now this simple fix will do.
|
||||
const reportCopy:any = {};
|
||||
for (const n in this.progressReport_) reportCopy[n] = this.progressReport_[n];
|
||||
if (reportCopy.errors) reportCopy.errors = this.progressReport_.errors.slice();
|
||||
this.dispatch({ type: 'SYNC_REPORT_UPDATE', report: reportCopy });
|
||||
}
|
||||
|
||||
async logSyncSummary(report) {
|
||||
async logSyncSummary(report:any) {
|
||||
this.logger().info('Operations completed: ');
|
||||
for (const n in report) {
|
||||
if (!report.hasOwnProperty(n)) continue;
|
||||
@ -186,9 +213,9 @@ class Synchronizer {
|
||||
this.cancelling_ = true;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
if (this.state() == 'idle') {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
@ -210,28 +237,27 @@ class Synchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
static stateToLabel(state) {
|
||||
static stateToLabel(state:string) {
|
||||
if (state === 'idle') return _('Idle');
|
||||
if (state === 'in_progress') return _('In progress');
|
||||
return state;
|
||||
}
|
||||
|
||||
isFullSync(steps) {
|
||||
isFullSync(steps:string[]) {
|
||||
return steps.includes('update_remote') && steps.includes('delete_remote') && steps.includes('delta');
|
||||
}
|
||||
|
||||
// TODO: test lockErrorStatus_
|
||||
async lockErrorStatus_() {
|
||||
const hasActiveExclusiveLock = await this.lockHandler().hasActiveLock('exclusive');
|
||||
const hasActiveExclusiveLock = await this.lockHandler().hasActiveLock(LockType.Exclusive);
|
||||
if (hasActiveExclusiveLock) return 'hasExclusiveLock';
|
||||
|
||||
const hasActiveSyncLock = await this.lockHandler().hasActiveLock('sync', this.appType_, this.clientId_);
|
||||
const hasActiveSyncLock = await this.lockHandler().hasActiveLock(LockType.Sync, this.appType_, this.clientId_);
|
||||
if (!hasActiveSyncLock) return 'syncLockGone';
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
async apiCall(fnName, ...args) {
|
||||
async apiCall(fnName:string, ...args:any[]) {
|
||||
if (this.syncTargetIsLocked_) throw new JoplinError('Sync target is locked - aborting API call', 'lockError');
|
||||
|
||||
try {
|
||||
@ -255,11 +281,11 @@ class Synchronizer {
|
||||
// 1. UPLOAD: Send to the sync target the items that have changed since the last sync.
|
||||
// 2. DELETE_REMOTE: Delete on the sync target, the items that have been deleted locally.
|
||||
// 3. DELTA: Find on the sync target the items that have been modified or deleted and apply the changes locally.
|
||||
async start(options = null) {
|
||||
async start(options:any = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
if (this.state() != 'idle') {
|
||||
const error = new Error(sprintf('Synchronisation is already in progress. State: %s', this.state()));
|
||||
const error:any = new Error(sprintf('Synchronisation is already in progress. State: %s', this.state()));
|
||||
error.code = 'alreadyStarted';
|
||||
throw error;
|
||||
}
|
||||
@ -292,12 +318,12 @@ class Synchronizer {
|
||||
|
||||
this.logSyncOperation('starting', null, null, `Starting synchronisation to target ${syncTargetId}... [${synchronizationId}]`);
|
||||
|
||||
const handleCannotSyncItem = async (ItemClass, syncTargetId, item, cannotSyncReason, itemLocation = null) => {
|
||||
const handleCannotSyncItem = async (ItemClass:any, syncTargetId:any, item:any, cannotSyncReason:string, itemLocation:any = null) => {
|
||||
await ItemClass.saveSyncDisabled(syncTargetId, item, cannotSyncReason, itemLocation);
|
||||
this.dispatch({ type: 'SYNC_HAS_DISABLED_SYNC_ITEMS' });
|
||||
};
|
||||
|
||||
const resourceRemotePath = resourceId => {
|
||||
const resourceRemotePath = (resourceId:string) => {
|
||||
return `${Dirnames.Resources}/${resourceId}`;
|
||||
};
|
||||
|
||||
@ -323,9 +349,9 @@ class Synchronizer {
|
||||
throw error;
|
||||
}
|
||||
|
||||
syncLock = await this.lockHandler().acquireLock('sync', this.appType_, this.clientId_);
|
||||
syncLock = await this.lockHandler().acquireLock(LockType.Sync, this.appType_, this.clientId_);
|
||||
|
||||
this.lockHandler().startAutoLockRefresh(syncLock, (error) => {
|
||||
this.lockHandler().startAutoLockRefresh(syncLock, (error:any) => {
|
||||
this.logger().warn('Could not refresh lock - cancelling sync. Error was:', error);
|
||||
this.syncTargetIsLocked_ = true;
|
||||
this.cancel();
|
||||
@ -339,9 +365,9 @@ class Synchronizer {
|
||||
// ========================================================================
|
||||
|
||||
if (syncSteps.indexOf('update_remote') >= 0) {
|
||||
const donePaths = [];
|
||||
const donePaths:string[] = [];
|
||||
|
||||
const completeItemProcessing = path => {
|
||||
const completeItemProcessing = (path:string) => {
|
||||
donePaths.push(path);
|
||||
};
|
||||
|
||||
@ -367,13 +393,13 @@ class Synchronizer {
|
||||
// (by setting an updated_time less than current time).
|
||||
if (donePaths.indexOf(path) >= 0) throw new JoplinError(sprintf('Processing a path that has already been done: %s. sync_time was not updated? Remote item has an updated_time in the future?', path), 'processingPathTwice');
|
||||
|
||||
const remote = await this.apiCall('stat', path);
|
||||
const remote:RemoteItem = await this.apiCall('stat', path);
|
||||
let action = null;
|
||||
|
||||
let reason = '';
|
||||
let remoteContent = null;
|
||||
|
||||
const getConflictType = (conflictedItem) => {
|
||||
const getConflictType = (conflictedItem:any) => {
|
||||
if (conflictedItem.type_ === BaseModel.TYPE_NOTE) return 'noteConflict';
|
||||
if (conflictedItem.type_ === BaseModel.TYPE_RESOURCE) return 'resourceConflict';
|
||||
return 'itemConflict';
|
||||
@ -626,7 +652,7 @@ class Synchronizer {
|
||||
while (true) {
|
||||
if (this.cancelling() || hasCancelled) break;
|
||||
|
||||
const listResult = await this.apiCall('delta', '', {
|
||||
const listResult:any = await this.apiCall('delta', '', {
|
||||
context: context,
|
||||
|
||||
// allItemIdsHandler() provides a way for drivers that don't have a delta API to
|
||||
@ -734,7 +760,7 @@ class Synchronizer {
|
||||
if (!content.user_updated_time) content.user_updated_time = content.updated_time;
|
||||
if (!content.user_created_time) content.user_created_time = content.created_time;
|
||||
|
||||
const options = {
|
||||
const options:any = {
|
||||
autoTimestamp: false,
|
||||
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, content, time.unixMs()),
|
||||
changeSource: ItemChange.SOURCE_SYNC,
|
||||
@ -857,7 +883,7 @@ class Synchronizer {
|
||||
|
||||
if (syncLock) {
|
||||
this.lockHandler().stopAutoLockRefresh(syncLock);
|
||||
await this.lockHandler().releaseLock('sync', this.appType_, this.clientId_);
|
||||
await this.lockHandler().releaseLock(LockType.Sync, this.appType_, this.clientId_);
|
||||
}
|
||||
|
||||
this.syncTargetIsLocked_ = false;
|
||||
@ -877,6 +903,7 @@ class Synchronizer {
|
||||
this.progressReport_ = {};
|
||||
|
||||
this.dispatch({ type: 'SYNC_COMPLETED', isFullSync: this.isFullSync(syncSteps) });
|
||||
eventManager.emit('syncComplete');
|
||||
|
||||
this.state_ = 'idle';
|
||||
|
||||
@ -885,5 +912,3 @@ class Synchronizer {
|
||||
return outputContext;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Synchronizer };
|
@ -1,6 +1,6 @@
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
class TaskQueue {
|
||||
constructor(name) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Mustache = require('mustache');
|
||||
|
||||
@ -47,7 +47,7 @@ TemplateUtils.loadTemplates = async function(filePath) {
|
||||
// sensitivity ensures that the sort will ignore case
|
||||
files.sort((a, b) => { return a.path.localeCompare(b.path, undefined, { sensitivity: 'accent' }); });
|
||||
|
||||
files.forEach(async file => {
|
||||
for (const file of files) {
|
||||
if (file.path.endsWith('.md')) {
|
||||
try {
|
||||
const fileString = await shim.fsDriver().readFile(`${filePath}/${file.path}`, 'utf-8');
|
||||
@ -59,7 +59,7 @@ TemplateUtils.loadTemplates = async function(filePath) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return templates;
|
||||
|
@ -1,5 +1,5 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const parseXmlString = require('xml2js').parseString;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const URL = require('url-parse');
|
||||
|
@ -1,10 +1,10 @@
|
||||
const welcomeAssets = require('./welcomeAssets');
|
||||
const Note = require('lib/models/Note');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const { shim } = require('lib/shim');
|
||||
const { uuid } = require('lib/uuid');
|
||||
const shim = require('lib/shim').default;
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { fileExtension, basename } = require('lib/path-utils');
|
||||
const { pregQuote } = require('lib/string-utils');
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration } from '../services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'historyBackward',
|
||||
@ -9,22 +9,22 @@ export const declaration:CommandDeclaration = {
|
||||
};
|
||||
|
||||
interface Props {
|
||||
backwardHistoryNotes: any[],
|
||||
hasBackwardNotes: boolean,
|
||||
}
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.backwardHistoryNotes.length) return;
|
||||
if (!props.hasBackwardNotes) return;
|
||||
utils.store.dispatch({
|
||||
type: 'HISTORY_BACKWARD',
|
||||
});
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return props.backwardHistoryNotes.length > 0;
|
||||
return props.hasBackwardNotes;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { backwardHistoryNotes: state.backwardHistoryNotes };
|
||||
return { hasBackwardNotes: state.backwardHistoryNotes.length > 0 };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration } from '../services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'historyForward',
|
||||
@ -8,22 +8,22 @@ export const declaration:CommandDeclaration = {
|
||||
};
|
||||
|
||||
interface Props {
|
||||
forwardHistoryNotes: any[],
|
||||
hasForwardNotes: boolean,
|
||||
}
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.forwardHistoryNotes.length) return;
|
||||
if (!props.hasForwardNotes) return;
|
||||
utils.store.dispatch({
|
||||
type: 'HISTORY_FORWARD',
|
||||
});
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return props.forwardHistoryNotes.length > 0;
|
||||
return props.hasForwardNotes;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { forwardHistoryNotes: state.forwardHistoryNotes };
|
||||
return { hasForwardNotes: state.forwardHistoryNotes.length > 0 };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration } from '../services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
import { _ } from 'lib/locale';
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
|
@ -4,9 +4,9 @@ const Component = React.Component;
|
||||
const { connect } = require('react-redux');
|
||||
const { View, TouchableOpacity, Text, Dimensions } = require('react-native');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { _ } = require('lib/locale');
|
||||
const shim = require('lib/shim').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
Icon.loadFont();
|
||||
|
||||
|
@ -5,7 +5,7 @@ const Note = require('lib/models/Note');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const ReactNativeActionButton = require('react-native-action-button').default;
|
||||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
Icon.loadFont();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { Platform } = require('react-native');
|
||||
const { themeById } = require('lib/theme');
|
||||
|
||||
|
@ -5,9 +5,9 @@ const Component = React.Component;
|
||||
const { Platform, View, Text } = require('react-native');
|
||||
const { WebView } = require('react-native-webview');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const { assetsToHeaders } = require('lib/joplin-renderer');
|
||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
@ -51,11 +51,11 @@ class NoteBodyViewer extends Component {
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
if (this.resourceLoadedTimeoutId_) {
|
||||
clearTimeout(this.resourceLoadedTimeoutId_);
|
||||
shim.clearTimeout(this.resourceLoadedTimeoutId_);
|
||||
this.resourceLoadedTimeoutId_ = null;
|
||||
}
|
||||
|
||||
this.resourceLoadedTimeoutId_ = setTimeout(() => {
|
||||
this.resourceLoadedTimeoutId_ = shim.setTimeout(() => {
|
||||
this.resourceLoadedTimeoutId_ = null;
|
||||
this.forceUpdate();
|
||||
}, 100);
|
||||
@ -87,16 +87,16 @@ class NoteBodyViewer extends Component {
|
||||
injectedJs.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
|
||||
injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
|
||||
injectedJs.push(`
|
||||
const readyStateCheckInterval = setInterval(function() {
|
||||
const readyStateCheckInterval = shim.setInterval(function() {
|
||||
if (document.readyState === "complete") {
|
||||
clearInterval(readyStateCheckInterval);
|
||||
shim.clearInterval(readyStateCheckInterval);
|
||||
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
|
||||
|
||||
const hash = "${this.props.noteHash}";
|
||||
// Gives it a bit of time before scrolling to the anchor
|
||||
// so that images are loaded.
|
||||
if (hash) {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
const e = document.getElementById(hash);
|
||||
if (!e) {
|
||||
console.warn('Cannot find hash', hash);
|
||||
@ -152,7 +152,7 @@ class NoteBodyViewer extends Component {
|
||||
}
|
||||
|
||||
onLoadEnd() {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
if (this.props.onLoadEnd) this.props.onLoadEnd();
|
||||
}, 100);
|
||||
|
||||
@ -160,7 +160,7 @@ class NoteBodyViewer extends Component {
|
||||
|
||||
// Need to display after a delay to avoid a white flash before
|
||||
// the content is displayed.
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
if (!this.isMounted_) return;
|
||||
this.setState({ webViewLoaded: true });
|
||||
}, 100);
|
||||
|
@ -2,7 +2,7 @@ const React = require('react');
|
||||
const Component = React.Component;
|
||||
const { connect } = require('react-redux');
|
||||
const { FlatList, Text, StyleSheet, Button, View } = require('react-native');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { NoteItem } = require('lib/components/note-item.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
|
@ -6,8 +6,8 @@ const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { BackButtonService } = require('lib/services/back-button.js');
|
||||
const NavService = require('lib/services/NavService.js');
|
||||
const { Menu, MenuOptions, MenuOption, MenuTrigger } = require('react-native-popup-menu');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
|
@ -3,7 +3,7 @@ const React = require('react');
|
||||
const { StyleSheet, View, Text, FlatList, TouchableOpacity, TextInput } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const ModalDialog = require('lib/components/ModalDialog');
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import useSyncTargetUpgrade from 'lib/services/synchronizer/gui/useSyncTargetUpgrade';
|
||||
import { _ } from 'lib/locale';
|
||||
const { View, Text, ScrollView } = require('react-native');
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
function UpgradeSyncTargetScreen(props:any) {
|
||||
const upgradeResult = useSyncTargetUpgrade();
|
||||
|
@ -3,11 +3,11 @@ const React = require('react');
|
||||
const { Platform, TouchableOpacity, Linking, View, Switch, StyleSheet, Text, Button, ScrollView, TextInput, Alert, PermissionsAndroid } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { Dropdown } = require('lib/components/Dropdown.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shared = require('lib/components/shared/config-shared.js');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
const { reg } = require('lib/registry.js');
|
||||
@ -15,7 +15,7 @@ const NavService = require('lib/services/NavService.js');
|
||||
const VersionInfo = require('react-native-version-info').default;
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const { time } = require('lib/time-utils');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const RNFS = require('react-native-fs');
|
||||
const checkPermissions = require('lib/checkPermissions.js').default;
|
||||
|
@ -3,7 +3,7 @@ const React = require('react');
|
||||
const { View, Button, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
|
@ -4,7 +4,7 @@ const { TextInput, TouchableOpacity, Linking, View, StyleSheet, Text, Button, Sc
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
@ -17,8 +17,8 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
||||
return { header: null };
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
passwordPromptShow: false,
|
||||
@ -26,7 +26,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
||||
passwordPromptConfirmAnswer: '',
|
||||
};
|
||||
|
||||
shared.constructor(this);
|
||||
shared.constructor(this, props);
|
||||
|
||||
this.styles_ = {};
|
||||
}
|
||||
@ -107,7 +107,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
||||
return shared.onPasswordChange(this, mk, text);
|
||||
};
|
||||
|
||||
const password = this.props.passwords[mk.id] ? this.props.passwords[mk.id] : '';
|
||||
const password = this.state.passwords[mk.id] ? this.state.passwords[mk.id] : '';
|
||||
const passwordOk = this.state.passwordChecks[mk.id] === true ? '✔' : '❌';
|
||||
|
||||
const inputStyle = { flex: 1, marginRight: 10, color: theme.color };
|
||||
|
@ -8,7 +8,7 @@ const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class FolderScreenComponent extends BaseScreenComponent {
|
||||
static navigationOptions() {
|
||||
|
@ -6,9 +6,9 @@ const { reg } = require('lib/registry.js');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { time } = require('lib/time-utils');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class LogScreenComponent extends BaseScreenComponent {
|
||||
static navigationOptions() {
|
||||
|
@ -4,13 +4,13 @@ import AsyncActionQueue from '../../AsyncActionQueue';
|
||||
const React = require('react');
|
||||
const { Platform, Clipboard, Keyboard, View, TextInput, StyleSheet, Linking, Image, Share } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { MarkdownEditor } = require('../../../MarkdownEditor/index.js');
|
||||
const RNFS = require('react-native-fs');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const UndoRedoService = require('lib/services/UndoRedoService.js').default;
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const md5 = require('md5');
|
||||
@ -24,9 +24,9 @@ const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const NoteTagsDialog = require('lib/components/screens/NoteTagsDialog');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Checkbox } = require('lib/components/checkbox.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { themeStyle, editorFont } = require('lib/components/global-style.js');
|
||||
@ -167,7 +167,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
type: 'NAV_BACK',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Note',
|
||||
@ -894,16 +894,16 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
}
|
||||
|
||||
scheduleFocusUpdate() {
|
||||
if (this.focusUpdateIID_) clearTimeout(this.focusUpdateIID_);
|
||||
if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_);
|
||||
|
||||
this.focusUpdateIID_ = setTimeout(() => {
|
||||
this.focusUpdateIID_ = shim.setTimeout(() => {
|
||||
this.focusUpdateIID_ = null;
|
||||
this.focusUpdate();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
focusUpdate() {
|
||||
if (this.focusUpdateIID_) clearTimeout(this.focusUpdateIID_);
|
||||
if (this.focusUpdateIID_) shim.clearTimeout(this.focusUpdateIID_);
|
||||
this.focusUpdateIID_ = null;
|
||||
|
||||
if (!this.state.note) return;
|
||||
@ -1005,9 +1005,9 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
}}
|
||||
onMarkForDownload={this.onMarkForDownload}
|
||||
onLoadEnd={() => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.setState({ HACK_webviewLoadingState: 1 });
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.setState({ HACK_webviewLoadingState: 0 });
|
||||
}, 50);
|
||||
}, 5);
|
||||
@ -1062,9 +1062,9 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
},
|
||||
onMarkForDownload: this.onMarkForDownload,
|
||||
onLoadEnd: () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.setState({ HACK_webviewLoadingState: 1 });
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.setState({ HACK_webviewLoadingState: 0 });
|
||||
}, 50);
|
||||
}, 5);
|
||||
|
@ -1,16 +1,16 @@
|
||||
const React = require('react');
|
||||
|
||||
const { AppState, View, StyleSheet } = require('react-native');
|
||||
const { stateUtils } = require('lib/reducer.js');
|
||||
const { stateUtils } = require('lib/reducer');
|
||||
const { connect } = require('react-redux');
|
||||
const { NoteList } = require('lib/components/note-list.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { ActionButton } = require('lib/components/action-button.js');
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
|
@ -6,10 +6,11 @@ const { WebView } = require('react-native-webview');
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const parseUri = require('lib/parseUri');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class OneDriveLoginScreenComponent extends BaseScreenComponent {
|
||||
static navigationOptions() {
|
||||
@ -91,7 +92,7 @@ class OneDriveLoginScreenComponent extends BaseScreenComponent {
|
||||
});
|
||||
this.forceUpdate();
|
||||
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.setState({
|
||||
webviewUrl: this.startUrl(),
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ const { StyleSheet, View, TextInput, FlatList, TouchableHighlight } = require('r
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { NoteItem } = require('lib/components/note-item.js');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
|
@ -1,11 +1,11 @@
|
||||
const React = require('react');
|
||||
|
||||
const { View, Text, Button, FlatList } = require('react-native');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { connect } = require('react-redux');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
|
||||
|
@ -5,7 +5,7 @@ const { connect } = require('react-redux');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
|
||||
class TagsScreenComponent extends BaseScreenComponent {
|
||||
|
@ -1,7 +1,7 @@
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
const ObjectUtils = require('lib/ObjectUtils');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { createSelector } = require('reselect');
|
||||
const { reg } = require('lib/registry');
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
class Shared {
|
||||
constructor(comp, showInfoMessageBox, showErrorMessageBox) {
|
||||
|
@ -1,19 +1,21 @@
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const MasterKey = require('lib/models/MasterKey.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const shared = {};
|
||||
|
||||
shared.constructor = function(comp) {
|
||||
shared.constructor = function(comp, props) {
|
||||
comp.state = {
|
||||
passwordChecks: {},
|
||||
stats: {
|
||||
encrypted: null,
|
||||
total: null,
|
||||
},
|
||||
passwords: Object.assign({}, props.passwords),
|
||||
};
|
||||
comp.isMounted_ = false;
|
||||
|
||||
@ -49,7 +51,7 @@ shared.upgradeMasterKey = async function(comp, masterKey) {
|
||||
}
|
||||
|
||||
try {
|
||||
const password = comp.props.passwords[masterKey.id];
|
||||
const password = comp.state.passwords[masterKey.id];
|
||||
const newMasterKey = await EncryptionService.instance().upgradeMasterKey(masterKey, password);
|
||||
await MasterKey.save(newMasterKey);
|
||||
reg.waitForSyncFinishedThenSync();
|
||||
@ -65,13 +67,13 @@ shared.componentDidMount = async function(comp) {
|
||||
shared.refreshStats(comp);
|
||||
|
||||
if (shared.refreshStatsIID_) {
|
||||
clearInterval(shared.refreshStatsIID_);
|
||||
shim.clearInterval(shared.refreshStatsIID_);
|
||||
shared.refreshStatsIID_ = null;
|
||||
}
|
||||
|
||||
shared.refreshStatsIID_ = setInterval(() => {
|
||||
shared.refreshStatsIID_ = shim.setInterval(() => {
|
||||
if (!comp.isMounted_) {
|
||||
clearInterval(shared.refreshStatsIID_);
|
||||
shim.clearInterval(shared.refreshStatsIID_);
|
||||
shared.refreshStatsIID_ = null;
|
||||
return;
|
||||
}
|
||||
@ -80,6 +82,10 @@ shared.componentDidMount = async function(comp) {
|
||||
};
|
||||
|
||||
shared.componentDidUpdate = async function(comp, prevProps = null) {
|
||||
if (prevProps && comp.props.passwords !== prevProps.passwords) {
|
||||
comp.setState({ passwords: Object.assign({}, comp.props.passwords) });
|
||||
}
|
||||
|
||||
if (!prevProps || comp.props.masterKeys !== prevProps.masterKeys || comp.props.passwords !== prevProps.passwords) {
|
||||
comp.checkPasswords();
|
||||
}
|
||||
@ -87,7 +93,7 @@ shared.componentDidUpdate = async function(comp, prevProps = null) {
|
||||
|
||||
shared.componentWillUnmount = function() {
|
||||
if (shared.refreshStatsIID_) {
|
||||
clearInterval(shared.refreshStatsIID_);
|
||||
shim.clearInterval(shared.refreshStatsIID_);
|
||||
shared.refreshStatsIID_ = null;
|
||||
}
|
||||
};
|
||||
@ -96,7 +102,7 @@ shared.checkPasswords = async function(comp) {
|
||||
const passwordChecks = Object.assign({}, comp.state.passwordChecks);
|
||||
for (let i = 0; i < comp.props.masterKeys.length; i++) {
|
||||
const mk = comp.props.masterKeys[i];
|
||||
const password = comp.props.passwords[mk.id];
|
||||
const password = comp.state.passwords[mk.id];
|
||||
const ok = password ? await EncryptionService.instance().checkMasterKeyPassword(mk, password) : false;
|
||||
passwordChecks[mk.id] = ok;
|
||||
}
|
||||
@ -111,18 +117,18 @@ shared.decryptedStatText = function(comp) {
|
||||
};
|
||||
|
||||
shared.onSavePasswordClick = function(comp, mk) {
|
||||
const password = comp.props.passwords[mk.id];
|
||||
const password = comp.state.passwords[mk.id];
|
||||
if (!password) {
|
||||
Setting.deleteObjectKey('encryption.passwordCache', mk.id);
|
||||
Setting.deleteObjectValue('encryption.passwordCache', mk.id);
|
||||
} else {
|
||||
Setting.setObjectKey('encryption.passwordCache', mk.id, password);
|
||||
Setting.setObjectValue('encryption.passwordCache', mk.id, password);
|
||||
}
|
||||
|
||||
comp.checkPasswords();
|
||||
};
|
||||
|
||||
shared.onPasswordChange = function(comp, mk, password) {
|
||||
const passwords = comp.props.passwords;
|
||||
const passwords = Object.assign({}, comp.state.passwords);
|
||||
passwords[mk.id] = password;
|
||||
comp.setState({ passwords: passwords });
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
const shared = {};
|
||||
|
@ -1,14 +1,17 @@
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Tag = require('lib/models/Tag');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const Note = require('lib/models/Note');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const eventManager = require('lib/eventManager').default;
|
||||
|
||||
const reduxSharedMiddleware = async function(store, next, action) {
|
||||
const newState = store.getState();
|
||||
|
||||
eventManager.appStateEmit(newState);
|
||||
|
||||
let refreshTags = false;
|
||||
|
||||
if (action.type == 'FOLDER_SET_COLLAPSED' || action.type == 'FOLDER_TOGGLE') {
|
||||
|
@ -4,9 +4,9 @@ const { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View,
|
||||
const { connect } = require('react-redux');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const NavService = require('lib/services/NavService.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Database {
|
||||
constructor(driver) {
|
||||
@ -83,7 +84,7 @@ class Database {
|
||||
if (this.profilingEnabled_) {
|
||||
console.info(`SQL START ${queryId}`, sql, params);
|
||||
|
||||
profilingTimeoutId = setInterval(() => {
|
||||
profilingTimeoutId = shim.setInterval(() => {
|
||||
console.warn(`SQL ${queryId} has been running for ${Date.now() - callStartTime}: ${sql}`);
|
||||
}, 3000);
|
||||
}
|
||||
@ -91,7 +92,7 @@ class Database {
|
||||
const result = await this.driver()[callName](sql, params);
|
||||
|
||||
if (this.profilingEnabled_) {
|
||||
clearInterval(profilingTimeoutId);
|
||||
shim.clearInterval(profilingTimeoutId);
|
||||
profilingTimeoutId = null;
|
||||
const elapsed = Date.now() - callStartTime;
|
||||
if (elapsed > 10) console.info(`SQL END ${queryId}`, elapsed, sql, params);
|
||||
@ -112,7 +113,7 @@ class Database {
|
||||
throw this.sqliteErrorToJsError(error, sql, params);
|
||||
}
|
||||
} finally {
|
||||
if (profilingTimeoutId) clearInterval(profilingTimeoutId);
|
||||
if (profilingTimeoutId) shim.clearInterval(profilingTimeoutId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
ReactNativeClient/lib/errorUtils.ts
Normal file
31
ReactNativeClient/lib/errorUtils.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
// This wraps an error message, allowing to set a prefix,
|
||||
// while preserving all the important properties
|
||||
// in particular the stack trace and original error message.
|
||||
export function wrapError(prefix:string, error:any) {
|
||||
if (!error) throw new Error('Unknown error');
|
||||
const newError:any = new Error([prefix, error.message || ''].join(': '));
|
||||
|
||||
if ('name' in error) newError.name = error.name;
|
||||
if ('fileName' in error) newError.fileName = error.fileName;
|
||||
if ('lineNumber' in error) newError.lineNumber = error.lineNumber;
|
||||
if ('columnNumber' in error) newError.columnNumber = error.columnNumber;
|
||||
|
||||
// "code" is a non-standard property that is used in Joplin
|
||||
if ('code' in error) newError.code = error.code;
|
||||
|
||||
// The stack is a string in this format:
|
||||
//
|
||||
// Error message
|
||||
// Stack line 1
|
||||
// Stack line 2
|
||||
// etc.
|
||||
//
|
||||
// And when console.error is used to print the error, it will take the message
|
||||
// from the stack (not from the "message" property), so it means we also need
|
||||
// to add the prefix error message to the stack.
|
||||
if ('stack' in error) newError.stack = [prefix, error.stack].join(': ');
|
||||
|
||||
return newError;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
const events = require('events');
|
||||
|
||||
class EventManager {
|
||||
|
||||
constructor() {
|
||||
this.emitter_ = new events.EventEmitter();
|
||||
}
|
||||
|
||||
on(eventName, callback) {
|
||||
return this.emitter_.on(eventName, callback);
|
||||
}
|
||||
|
||||
emit(eventName, object = null) {
|
||||
return this.emitter_.emit(eventName, object);
|
||||
}
|
||||
|
||||
removeListener(eventName, callback) {
|
||||
return this.emitter_.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
off(eventName, callback) {
|
||||
return this.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const eventManager = new EventManager();
|
||||
|
||||
module.exports = eventManager;
|
129
ReactNativeClient/lib/eventManager.ts
Normal file
129
ReactNativeClient/lib/eventManager.ts
Normal file
@ -0,0 +1,129 @@
|
||||
const events = require('events');
|
||||
|
||||
class EventManager {
|
||||
|
||||
private emitter_:any;
|
||||
private appStatePrevious_:any;
|
||||
private appStateWatchedProps_:string[];
|
||||
private appStateListeners_:any;
|
||||
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.emitter_ = new events.EventEmitter();
|
||||
|
||||
this.appStatePrevious_ = {};
|
||||
this.appStateWatchedProps_ = [];
|
||||
this.appStateListeners_ = {};
|
||||
}
|
||||
|
||||
on(eventName:string, callback:Function) {
|
||||
return this.emitter_.on(eventName, callback);
|
||||
}
|
||||
|
||||
emit(eventName:string, object:any = null) {
|
||||
return this.emitter_.emit(eventName, object);
|
||||
}
|
||||
|
||||
removeListener(eventName:string, callback:Function) {
|
||||
return this.emitter_.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
off(eventName:string, callback:Function) {
|
||||
return this.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
filterOn(filterName:string, callback:Function) {
|
||||
return this.emitter_.on(`filter:${filterName}`, callback);
|
||||
}
|
||||
|
||||
filterOff(filterName:string, callback:Function) {
|
||||
return this.removeListener(`filter:${filterName}`, callback);
|
||||
}
|
||||
|
||||
filterEmit(filterName:string, object:any) {
|
||||
// We freeze the object we pass to the listeners so that they
|
||||
// don't modify it directly. Instead they must return a
|
||||
// modified copy (or the input itself).
|
||||
let output = Object.freeze(object);
|
||||
const listeners = this.emitter_.listeners(`filter:${filterName}`);
|
||||
for (const listener of listeners) {
|
||||
const newOutput = listener(output);
|
||||
|
||||
if (newOutput === undefined) {
|
||||
throw new Error(`Filter "${filterName}": Filter must return a value or the unmodified input. Returning nothing or "undefined" is not supported.`);
|
||||
}
|
||||
|
||||
if (newOutput !== output) {
|
||||
output = Object.freeze(newOutput);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
appStateOn(propName:string, callback:Function) {
|
||||
if (!this.appStateListeners_[propName]) {
|
||||
this.appStateListeners_[propName] = [];
|
||||
this.appStateWatchedProps_.push(propName);
|
||||
}
|
||||
|
||||
this.appStateListeners_[propName].push(callback);
|
||||
}
|
||||
|
||||
appStateOff(propName:string, callback:Function) {
|
||||
if (!this.appStateListeners_[propName]) {
|
||||
throw new Error('EventManager: Trying to unregister a state prop watch for a non-watched prop (1)');
|
||||
}
|
||||
|
||||
const idx = this.appStateListeners_[propName].indexOf(callback);
|
||||
if (idx < 0) throw new Error('EventManager: Trying to unregister a state prop watch for a non-watched prop (2)');
|
||||
|
||||
this.appStateListeners_[propName].splice(idx, 1);
|
||||
}
|
||||
|
||||
stateValue_(state:any, propName:string) {
|
||||
const parts = propName.split('.');
|
||||
let s = state;
|
||||
for (const p of parts) {
|
||||
if (!(p in s)) throw new Error(`Invalid state property path: ${propName}`);
|
||||
s = s[p];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// The appStateEmit function should be called from a middleware.
|
||||
appStateEmit(state:any) {
|
||||
if (!this.appStateWatchedProps_.length) return;
|
||||
|
||||
for (const propName of this.appStateWatchedProps_) {
|
||||
let emit = false;
|
||||
|
||||
const stateValue = this.stateValue_(state, propName);
|
||||
|
||||
if (!(propName in this.appStatePrevious_) || this.appStatePrevious_[propName] !== stateValue) {
|
||||
this.appStatePrevious_[propName] = stateValue;
|
||||
emit = true;
|
||||
}
|
||||
|
||||
if (emit) {
|
||||
const listeners = this.appStateListeners_[propName];
|
||||
if (!listeners || !listeners.length) continue;
|
||||
|
||||
const eventValue = Object.freeze(stateValue);
|
||||
for (const listener of listeners) {
|
||||
listener({ value: eventValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const eventManager = new EventManager();
|
||||
|
||||
export default eventManager;
|
@ -1,6 +1,6 @@
|
||||
const { basicDelta } = require('lib/file-api');
|
||||
const { basename } = require('lib/path-utils');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
|
||||
const S3_MAX_DELETES = 1000;
|
||||
|
@ -1,5 +1,5 @@
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
|
||||
class FileApiDriverDropbox {
|
||||
|
@ -1,6 +1,6 @@
|
||||
const moment = require('moment');
|
||||
const { dirname, basename } = require('lib/path-utils.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class FileApiDriverOneDrive {
|
||||
constructor(api) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
const { isHidden } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
|
@ -1,5 +1,6 @@
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class FoldersScreenUtils {
|
||||
static async allForDisplay(options = {}) {
|
||||
@ -48,8 +49,8 @@ class FoldersScreenUtils {
|
||||
}
|
||||
|
||||
static scheduleRefreshFolders() {
|
||||
if (this.scheduleRefreshFoldersIID_) clearTimeout(this.scheduleRefreshFoldersIID_);
|
||||
this.scheduleRefreshFoldersIID_ = setTimeout(() => {
|
||||
if (this.scheduleRefreshFoldersIID_) shim.clearTimeout(this.scheduleRefreshFoldersIID_);
|
||||
this.scheduleRefreshFoldersIID_ = shim.setTimeout(() => {
|
||||
this.scheduleRefreshFoldersIID_ = null;
|
||||
this.refreshFolders();
|
||||
}, 1000);
|
||||
@ -57,13 +58,13 @@ class FoldersScreenUtils {
|
||||
|
||||
static async cancelTimers() {
|
||||
if (this.scheduleRefreshFoldersIID_) {
|
||||
clearTimeout(this.scheduleRefreshFoldersIID_);
|
||||
shim.clearTimeout(this.scheduleRefreshFoldersIID_);
|
||||
this.scheduleRefreshFoldersIID_ = null;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
if (!FoldersScreenUtils.refreshCalls_.length) {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
@ -1,6 +1,6 @@
|
||||
const { filename, fileExtension } = require('lib/path-utils');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const md5 = require('md5');
|
||||
|
||||
class FsDriverBase {
|
||||
|
@ -188,6 +188,11 @@ class FsDriverNode extends FsDriverBase {
|
||||
if (encoding === 'ascii') return buffer.toString('ascii');
|
||||
throw new Error(`Unsupported encoding: ${encoding}`);
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
return require('path').resolve(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.FsDriverNode = FsDriverNode;
|
||||
|
@ -156,6 +156,10 @@ class FsDriverRN extends FsDriverBase {
|
||||
handle.offset += length;
|
||||
return output ? output : null;
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
throw new Error(`Not implemented: resolve(): ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.FsDriverRN = FsDriverRN;
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class GeolocationNode {
|
||||
static async currentPosition(options = null) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Geolocation from '@react-native-community/geolocation';
|
||||
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
class GeolocationReact {
|
||||
static currentPosition_testResponse() {
|
||||
|
@ -1,10 +1,10 @@
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const moment = require('moment');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
const { enexXmlToMd } = require('./import-enex-md-gen.js');
|
||||
const { enexXmlToHtml } = require('./import-enex-html-gen.js');
|
||||
@ -13,6 +13,7 @@ const Levenshtein = require('levenshtein');
|
||||
const md5 = require('md5');
|
||||
const { Base64Decode } = require('base64-stream');
|
||||
const md5File = require('md5-file');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
// const Promise = require('promise');
|
||||
const fs = require('fs-extra');
|
||||
@ -484,10 +485,10 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
|
||||
saxStream.on('end', function() {
|
||||
// Wait till there is no more notes to process.
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
processNotes().then(allDone => {
|
||||
if (allDone) {
|
||||
clearTimeout(iid);
|
||||
shim.clearTimeout(iid);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ const { promiseChain } = require('lib/promise-utils.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const structureSql = `
|
||||
CREATE TABLE folders (
|
||||
@ -866,7 +866,7 @@ class JoplinDatabase extends Database {
|
||||
this.logger().warn('Could not upgrade to database v15 or v18 or v33 - FTS feature will not be used', error);
|
||||
saveVersionAgain = true;
|
||||
} else if (targetVersion === 34) {
|
||||
this.logger().warn('Could not upgrade to database v34 - fuzzy search will not be used', error);
|
||||
if (!shim.isTestingEnv()) this.logger().warn('Could not upgrade to database v34 - fuzzy search will not be used', error);
|
||||
saveVersionAgain = true;
|
||||
} else {
|
||||
throw error;
|
||||
|
@ -1,7 +1,7 @@
|
||||
const htmlUtils = require('./htmlUtils');
|
||||
const utils = require('./utils');
|
||||
const noteStyle = require('./noteStyle');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const memoryCache = require('memory-cache');
|
||||
const md5 = require('md5');
|
||||
|
@ -19,6 +19,7 @@ const rules = {
|
||||
mermaid: require('./MdToHtml/rules/mermaid').default,
|
||||
};
|
||||
|
||||
// const eventManager = require('lib/eventManager').default;
|
||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||
const hljs = require('highlight.js');
|
||||
const nodeSlug = require('slug');
|
||||
@ -298,9 +299,16 @@ class MdToHtml {
|
||||
markdownIt.use(markdownItAnchor, { slugify: slugify });
|
||||
|
||||
for (const key in plugins) {
|
||||
if (this.pluginEnabled(key)) markdownIt.use(plugins[key].module, plugins[key].options);
|
||||
if (this.pluginEnabled(key)) {
|
||||
markdownIt.use(plugins[key].module, plugins[key].options);
|
||||
}
|
||||
}
|
||||
|
||||
// const extraPlugins = eventManager.filterEmit('mdToHtmlPlugins', {});
|
||||
// for (const key in extraPlugins) {
|
||||
// markdownIt.use(extraPlugins[key].module, extraPlugins[key].options);
|
||||
// }
|
||||
|
||||
setupLinkify(markdownIt);
|
||||
|
||||
const renderedBody = markdownIt.render(body);
|
||||
|
@ -1,9 +1,4 @@
|
||||
function formatCssSize(v) {
|
||||
if (typeof v === 'string') {
|
||||
if (v.includes('px') || v.includes('em') || v.includes('%')) return v;
|
||||
}
|
||||
return `${v}px`;
|
||||
}
|
||||
const { formatCssSize } = require('lib/string-utils');
|
||||
|
||||
module.exports = function(theme) {
|
||||
theme = theme ? theme : {};
|
||||
|
@ -1,6 +1,10 @@
|
||||
const { sprintf } = require('sprintf-js');
|
||||
|
||||
const codeToLanguageE_ = {};
|
||||
interface StringToStringMap {
|
||||
[key:string]: string,
|
||||
}
|
||||
|
||||
const codeToLanguageE_:StringToStringMap = {};
|
||||
codeToLanguageE_['aa'] = 'Afar';
|
||||
codeToLanguageE_['ab'] = 'Abkhazian';
|
||||
codeToLanguageE_['af'] = 'Afrikaans';
|
||||
@ -144,7 +148,7 @@ codeToLanguageE_['za'] = 'Zhuang';
|
||||
codeToLanguageE_['zh'] = 'Chinese';
|
||||
codeToLanguageE_['zu'] = 'Zulu';
|
||||
|
||||
const codeToLanguage_ = {};
|
||||
const codeToLanguage_:StringToStringMap = {};
|
||||
codeToLanguage_['an'] = 'Aragonés';
|
||||
codeToLanguage_['da'] = 'Dansk';
|
||||
codeToLanguage_['de'] = 'Deutsch';
|
||||
@ -172,17 +176,17 @@ codeToLanguage_['et'] = 'Eesti Keel';
|
||||
codeToLanguage_['vi'] = 'Tiếng Việt';
|
||||
codeToLanguage_['hu'] = 'Magyar';
|
||||
|
||||
const codeToCountry_ = {};
|
||||
const codeToCountry_:StringToStringMap = {};
|
||||
codeToCountry_['BR'] = 'Brasil';
|
||||
codeToCountry_['CR'] = 'Costa Rica';
|
||||
codeToCountry_['CN'] = '中国';
|
||||
codeToCountry_['GB'] = 'UK';
|
||||
codeToCountry_['US'] = 'US';
|
||||
|
||||
let supportedLocales_ = null;
|
||||
let localeStats_ = null;
|
||||
let supportedLocales_:any = null;
|
||||
let localeStats_:any = null;
|
||||
|
||||
const loadedLocales_ = {};
|
||||
const loadedLocales_:any = {};
|
||||
|
||||
const defaultLocale_ = 'en_GB';
|
||||
|
||||
@ -197,7 +201,7 @@ function localeStats() {
|
||||
return localeStats_;
|
||||
}
|
||||
|
||||
function supportedLocales() {
|
||||
function supportedLocales():string[] {
|
||||
if (!supportedLocales_) supportedLocales_ = require('../locales/index.js').locales;
|
||||
|
||||
const output = [];
|
||||
@ -208,11 +212,15 @@ function supportedLocales() {
|
||||
return output;
|
||||
}
|
||||
|
||||
function supportedLocalesToLanguages(options = null) {
|
||||
interface SupportedLocalesToLanguagesOptions {
|
||||
includeStats?: boolean,
|
||||
}
|
||||
|
||||
function supportedLocalesToLanguages(options:SupportedLocalesToLanguagesOptions = null) {
|
||||
if (!options) options = {};
|
||||
const stats = localeStats();
|
||||
const locales = supportedLocales();
|
||||
const output = {};
|
||||
const output:StringToStringMap = {};
|
||||
for (let i = 0; i < locales.length; i++) {
|
||||
const locale = locales[i];
|
||||
output[locale] = countryDisplayName(locale);
|
||||
@ -225,7 +233,7 @@ function supportedLocalesToLanguages(options = null) {
|
||||
return output;
|
||||
}
|
||||
|
||||
function closestSupportedLocale(canonicalName, defaultToEnglish = true, locales = null) {
|
||||
function closestSupportedLocale(canonicalName:string, defaultToEnglish:boolean = true, locales:string[] = null) {
|
||||
locales = locales === null ? supportedLocales() : locales;
|
||||
if (locales.indexOf(canonicalName) >= 0) return canonicalName;
|
||||
|
||||
@ -240,31 +248,31 @@ function closestSupportedLocale(canonicalName, defaultToEnglish = true, locales
|
||||
return defaultToEnglish ? 'en_GB' : null;
|
||||
}
|
||||
|
||||
function countryName(countryCode) {
|
||||
function countryName(countryCode:string) {
|
||||
return codeToCountry_[countryCode] ? codeToCountry_[countryCode] : '';
|
||||
}
|
||||
|
||||
function languageNameInEnglish(languageCode) {
|
||||
function languageNameInEnglish(languageCode:string) {
|
||||
return codeToLanguageE_[languageCode] ? codeToLanguageE_[languageCode] : '';
|
||||
}
|
||||
|
||||
function languageName(languageCode, defaultToEnglish = true) {
|
||||
function languageName(languageCode:string, defaultToEnglish:boolean = true) {
|
||||
if (codeToLanguage_[languageCode]) return codeToLanguage_[languageCode];
|
||||
if (defaultToEnglish) return languageNameInEnglish(languageCode);
|
||||
return '';
|
||||
}
|
||||
|
||||
function languageCodeOnly(canonicalName) {
|
||||
function languageCodeOnly(canonicalName:string) {
|
||||
if (canonicalName.length < 2) return canonicalName;
|
||||
return canonicalName.substr(0, 2);
|
||||
}
|
||||
|
||||
function countryCodeOnly(canonicalName) {
|
||||
function countryCodeOnly(canonicalName:string) {
|
||||
if (canonicalName.length <= 2) return '';
|
||||
return canonicalName.substr(3);
|
||||
}
|
||||
|
||||
function countryDisplayName(canonicalName) {
|
||||
function countryDisplayName(canonicalName:string) {
|
||||
const languageCode = languageCodeOnly(canonicalName);
|
||||
const countryCode = countryCodeOnly(canonicalName);
|
||||
|
||||
@ -287,7 +295,7 @@ function countryDisplayName(canonicalName) {
|
||||
return output;
|
||||
}
|
||||
|
||||
function localeStrings(canonicalName) {
|
||||
function localeStrings(canonicalName:string) {
|
||||
const locale = closestSupportedLocale(canonicalName);
|
||||
|
||||
if (loadedLocales_[locale]) return loadedLocales_[locale];
|
||||
@ -297,7 +305,7 @@ function localeStrings(canonicalName) {
|
||||
return loadedLocales_[locale];
|
||||
}
|
||||
|
||||
function setLocale(canonicalName) {
|
||||
function setLocale(canonicalName:string) {
|
||||
if (currentLocale_ == canonicalName) return;
|
||||
currentLocale_ = closestSupportedLocale(canonicalName);
|
||||
}
|
||||
@ -306,7 +314,7 @@ function languageCode() {
|
||||
return languageCodeOnly(currentLocale_);
|
||||
}
|
||||
|
||||
function _(s, ...args) {
|
||||
function _(s:string, ...args:any[]) {
|
||||
const strings = localeStrings(currentLocale_);
|
||||
let result = strings[s];
|
||||
if (result === '' || result === undefined) result = s;
|
||||
@ -317,9 +325,9 @@ function _(s, ...args) {
|
||||
}
|
||||
}
|
||||
|
||||
function _n(singular, plural, n, ...args) {
|
||||
function _n(singular:string, plural:string, n:number, ...args:any[]) {
|
||||
if (n > 1) return _(plural, ...args);
|
||||
return _(singular, ...args);
|
||||
}
|
||||
|
||||
module.exports = { _, _n, supportedLocales, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
|
||||
export { _, _n, supportedLocales, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
|
@ -7,35 +7,45 @@ const { setupLinkify } = require('lib/joplin-renderer');
|
||||
const listRegex = /^(\s*)([*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]\s))(\s*)/;
|
||||
const emptyListRegex = /^(\s*)([*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/;
|
||||
|
||||
export interface MarkdownTableHeader {
|
||||
name: string,
|
||||
label: string,
|
||||
filter?: Function,
|
||||
}
|
||||
|
||||
export interface MarkdownTableRow {
|
||||
[key:string]: string,
|
||||
}
|
||||
|
||||
const markdownUtils = {
|
||||
// Titles for markdown links only need escaping for [ and ]
|
||||
escapeTitleText(text) {
|
||||
escapeTitleText(text:string) {
|
||||
return text.replace(/(\[|\])/g, '\\$1');
|
||||
},
|
||||
|
||||
escapeLinkUrl(url) {
|
||||
escapeLinkUrl(url:string) {
|
||||
url = url.replace(/\(/g, '%28');
|
||||
url = url.replace(/\)/g, '%29');
|
||||
url = url.replace(/ /g, '%20');
|
||||
return url;
|
||||
},
|
||||
|
||||
prependBaseUrl(md, baseUrl) {
|
||||
prependBaseUrl(md:string, baseUrl:string) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
return md.replace(/(\]\()([^\s\)]+)(.*?\))/g, (match, before, url, after) => {
|
||||
return md.replace(/(\]\()([^\s\)]+)(.*?\))/g, (_match:any, before:string, url:string, after:string) => {
|
||||
return before + urlUtils.prependBaseUrl(url, baseUrl) + after;
|
||||
});
|
||||
},
|
||||
|
||||
extractImageUrls(md) {
|
||||
extractImageUrls(md:string) {
|
||||
const markdownIt = new MarkdownIt();
|
||||
setupLinkify(markdownIt); // Necessary to support file:/// links
|
||||
|
||||
const env = {};
|
||||
const tokens = markdownIt.parse(md, env);
|
||||
const output = [];
|
||||
const output:string[] = [];
|
||||
|
||||
const searchUrls = tokens => {
|
||||
const searchUrls = (tokens:any[]) => {
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i];
|
||||
|
||||
@ -62,25 +72,25 @@ const markdownUtils = {
|
||||
// The match results has 5 items
|
||||
// Full match array is
|
||||
// [Full match, whitespace, list token, ol line number, whitespace following token]
|
||||
olLineNumber(line) {
|
||||
olLineNumber(line:string) {
|
||||
const match = line.match(listRegex);
|
||||
return match ? Number(match[3]) : 0;
|
||||
},
|
||||
|
||||
extractListToken(line) {
|
||||
extractListToken(line:string) {
|
||||
const match = line.match(listRegex);
|
||||
return match ? match[2] : '';
|
||||
},
|
||||
|
||||
isListItem(line) {
|
||||
isListItem(line:string) {
|
||||
return listRegex.test(line);
|
||||
},
|
||||
|
||||
isEmptyListItem(line) {
|
||||
isEmptyListItem(line:string) {
|
||||
return emptyListRegex.test(line);
|
||||
},
|
||||
|
||||
createMarkdownTable(headers, rows) {
|
||||
createMarkdownTable(headers:MarkdownTableHeader[], rows:MarkdownTableRow[]):string {
|
||||
const output = [];
|
||||
|
||||
const headersMd = [];
|
||||
@ -108,7 +118,7 @@ const markdownUtils = {
|
||||
return output.join('\n');
|
||||
},
|
||||
|
||||
titleFromBody(body) {
|
||||
titleFromBody(body:string) {
|
||||
if (!body) return '';
|
||||
const mdLinkRegex = /!?\[([^\]]+?)\]\(.+?\)/g;
|
||||
const emptyMdLinkRegex = /!?\[\]\((.+?)\)/g;
|
||||
@ -119,4 +129,4 @@ const markdownUtils = {
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = markdownUtils;
|
||||
export default markdownUtils;
|
@ -1,8 +1,8 @@
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
const htmlUtils = require('lib/htmlUtils');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Resource = require('lib/models/Resource');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
|
||||
class MarkupLanguageUtils {
|
||||
|
@ -1,6 +1,6 @@
|
||||
const Resource = require('lib/models/Resource');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
const script = {};
|
||||
|
||||
|
@ -1,7 +1,15 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Alarm extends BaseModel {
|
||||
export interface Notification {
|
||||
id: string,
|
||||
noteId: string,
|
||||
date: Date,
|
||||
title: string,
|
||||
body?: string,
|
||||
}
|
||||
|
||||
export default class Alarm extends BaseModel {
|
||||
static tableName() {
|
||||
return 'alarms';
|
||||
}
|
||||
@ -10,7 +18,7 @@ class Alarm extends BaseModel {
|
||||
return BaseModel.TYPE_ALARM;
|
||||
}
|
||||
|
||||
static byNoteId(noteId) {
|
||||
static byNoteId(noteId:string) {
|
||||
return this.modelSelectOne('SELECT * FROM alarms WHERE note_id = ?', [noteId]);
|
||||
}
|
||||
|
||||
@ -21,12 +29,12 @@ class Alarm extends BaseModel {
|
||||
static async alarmIdsWithoutNotes() {
|
||||
// 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');
|
||||
return alarms.map(a => {
|
||||
return alarms.map((a:any) => {
|
||||
return a.id;
|
||||
});
|
||||
}
|
||||
|
||||
static async makeNotification(alarm, note = null) {
|
||||
static async makeNotification(alarm:any, note:any = null):Promise<Notification> {
|
||||
if (!note) {
|
||||
note = await Note.load(alarm.note_id);
|
||||
} else if (!note.todo_due) {
|
||||
@ -35,8 +43,9 @@ class Alarm extends BaseModel {
|
||||
this.logger().warn('Reloaded note:', note);
|
||||
}
|
||||
|
||||
const output = {
|
||||
const output:Notification = {
|
||||
id: alarm.id,
|
||||
noteId: alarm.note_id,
|
||||
date: new Date(note.todo_due),
|
||||
title: note.title.substr(0, 128),
|
||||
};
|
||||
@ -50,5 +59,3 @@ class Alarm extends BaseModel {
|
||||
return this.modelSelectAll('SELECT * FROM alarms WHERE trigger_time >= ?', [Date.now()]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Alarm;
|
@ -1,13 +1,13 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const JoplinError = require('lib/JoplinError.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const moment = require('moment');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
|
||||
class BaseItem extends BaseModel {
|
||||
static useUuid() {
|
||||
|
@ -2,7 +2,7 @@ const BaseModel = require('lib/BaseModel.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const { substrWithEllipsis } = require('lib/string-utils.js');
|
||||
|
||||
@ -305,7 +305,7 @@ class Folder extends BaseItem {
|
||||
static buildTree(folders) {
|
||||
const idToFolders = {};
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
idToFolders[folders[i].id] = folders[i];
|
||||
idToFolders[folders[i].id] = Object.assign({}, folders[i]);
|
||||
idToFolders[folders[i].id].children = [];
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class ItemChange extends BaseModel {
|
||||
static tableName() {
|
||||
@ -37,9 +38,9 @@ class ItemChange extends BaseModel {
|
||||
// can be used for synchronous code, in particular when unit testing.
|
||||
static async waitForAllSaved() {
|
||||
return new Promise((resolve) => {
|
||||
const iid = setInterval(() => {
|
||||
const iid = shim.setInterval(() => {
|
||||
if (!ItemChange.saveCalls_.length) {
|
||||
clearInterval(iid);
|
||||
shim.clearInterval(iid);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
@ -3,15 +3,15 @@ const { sprintf } = require('sprintf-js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const { pregQuote } = require('lib/string-utils.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const lodash = require('lodash');
|
||||
const urlUtils = require('lib/urlUtils.js');
|
||||
const markdownUtils = require('lib/markdownUtils.js');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids');
|
||||
|
||||
|
@ -3,14 +3,14 @@ const BaseItem = require('lib/models/BaseItem.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const NoteResource = require('lib/models/NoteResource.js');
|
||||
const ResourceLocalState = require('lib/models/ResourceLocalState.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const { mime } = require('lib/mime-utils.js');
|
||||
const { filename, safeFilename } = require('lib/path-utils.js');
|
||||
const { FsDriverDummy } = require('lib/fs-driver-dummy.js');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class Resource extends BaseItem {
|
||||
static tableName() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const netUtils = {};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
const ntpClient = require('lib/vendor/ntp-client');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
let nextSyncTime = 0;
|
||||
|
@ -1,5 +1,6 @@
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const http = require('http');
|
||||
const urlParser = require('url');
|
||||
@ -71,7 +72,7 @@ class OneDriveApiNodeUtils {
|
||||
// away or the browser might display a connection reset error (even
|
||||
// though it worked).
|
||||
const waitAndDestroy = () => {
|
||||
setTimeout(() => {
|
||||
shim.setTimeout(() => {
|
||||
this.oauthServer_.destroy();
|
||||
this.oauthServer_ = null;
|
||||
}, 1000);
|
||||
|
@ -1,8 +1,8 @@
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const { stringify } = require('query-string');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class OneDriveApi {
|
||||
// `isPublic` is to tell OneDrive whether the application is a "public" one (Mobile and desktop
|
||||
|
@ -1,4 +1,4 @@
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
const parameters_ = {};
|
||||
|
||||
|
2
ReactNativeClient/lib/react-logger.js
vendored
2
ReactNativeClient/lib/react-logger.js
vendored
@ -1,4 +1,4 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
class ReactLogger extends Logger {}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1118
ReactNativeClient/lib/reducer.ts
Normal file
1118
ReactNativeClient/lib/reducer.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
|
||||
const reg = {};
|
||||
@ -78,7 +78,7 @@ reg.scheduleSync = async (delay = null, syncOptions = null) => {
|
||||
});
|
||||
|
||||
if (reg.scheduleSyncId_) {
|
||||
clearTimeout(reg.scheduleSyncId_);
|
||||
shim.clearTimeout(reg.scheduleSyncId_);
|
||||
reg.scheduleSyncId_ = null;
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ reg.scheduleSync = async (delay = null, syncOptions = null) => {
|
||||
if (delay === 0) {
|
||||
timeoutCallback();
|
||||
} else {
|
||||
reg.scheduleSyncId_ = setTimeout(timeoutCallback, delay);
|
||||
reg.scheduleSyncId_ = shim.setTimeout(timeoutCallback, delay);
|
||||
}
|
||||
return promise;
|
||||
|
||||
@ -204,7 +204,7 @@ reg.cancelTimers_ = () => {
|
||||
this.recurrentSyncId_ = null;
|
||||
}
|
||||
if (reg.scheduleSyncId_) {
|
||||
clearTimeout(reg.scheduleSyncId_);
|
||||
shim.clearTimeout(reg.scheduleSyncId_);
|
||||
reg.scheduleSyncId_ = null;
|
||||
}
|
||||
};
|
||||
@ -214,7 +214,7 @@ reg.cancelTimers = async () => {
|
||||
reg.cancelTimers_();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setInterval(() => {
|
||||
shim.setInterval(() => {
|
||||
// ensure processing complete
|
||||
if (!reg.setupRecurrentCalls_.length && !reg.schedSyncCalls_.length && !reg.timerCallbackCalls_.length && !reg.waitForReSyncCalls_.length) {
|
||||
reg.cancelTimers_();
|
||||
|
@ -1,8 +1,15 @@
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Alarm = require('lib/models/Alarm.js');
|
||||
import Logger from 'lib/Logger';
|
||||
import Alarm from 'lib/models/Alarm';
|
||||
|
||||
class AlarmService {
|
||||
static setDriver(v) {
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
export default class AlarmService {
|
||||
|
||||
private static driver_:any;
|
||||
private static logger_:Logger;
|
||||
// private static inAppNotificationHandler_:any;
|
||||
|
||||
static setDriver(v:any) {
|
||||
this.driver_ = v;
|
||||
|
||||
if (this.driver_.setService) this.driver_.setService(this);
|
||||
@ -13,7 +20,7 @@ class AlarmService {
|
||||
return this.driver_;
|
||||
}
|
||||
|
||||
static setLogger(v) {
|
||||
static setLogger(v:Logger) {
|
||||
this.logger_ = v;
|
||||
}
|
||||
|
||||
@ -21,8 +28,8 @@ class AlarmService {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
static setInAppNotificationHandler(v) {
|
||||
this.inAppNotificationHandler_ = v;
|
||||
static setInAppNotificationHandler(v:any) {
|
||||
// this.inAppNotificationHandler_ = v;
|
||||
if (this.driver_.setInAppNotificationHandler) this.driver_.setInAppNotificationHandler(v);
|
||||
}
|
||||
|
||||
@ -43,7 +50,7 @@ class AlarmService {
|
||||
|
||||
// When passing a note, make sure it has all the required properties
|
||||
// (better to pass a complete note or else just the ID)
|
||||
static async updateNoteNotification(noteOrId, isDeleted = false) {
|
||||
static async updateNoteNotification(noteOrId:any, isDeleted:boolean = false) {
|
||||
try {
|
||||
let note = null;
|
||||
let noteId = null;
|
||||
@ -118,5 +125,3 @@ class AlarmService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AlarmService;
|
@ -1,11 +1,16 @@
|
||||
import { Notification } from 'lib/models/Alarm';
|
||||
|
||||
const PushNotification = require('react-native-push-notification');
|
||||
|
||||
class AlarmServiceDriver {
|
||||
export default class AlarmServiceDriver {
|
||||
|
||||
private PushNotification_:any = null;
|
||||
|
||||
PushNotificationHandler_() {
|
||||
if (!this.PushNotification_) {
|
||||
PushNotification.configure({
|
||||
// (required) Called when a remote or local notification is opened or received
|
||||
onNotification: function(notification) {
|
||||
onNotification: function(notification:any) {
|
||||
console.info('Notification was opened: ', notification);
|
||||
// process the notification
|
||||
},
|
||||
@ -27,11 +32,11 @@ class AlarmServiceDriver {
|
||||
throw new Error('Available only for non-persistent alarms');
|
||||
}
|
||||
|
||||
async clearNotification(id) {
|
||||
async clearNotification(id:any) {
|
||||
return this.PushNotificationHandler_().cancelLocalNotifications({ id: `${id}` });
|
||||
}
|
||||
|
||||
async scheduleNotification(notification) {
|
||||
async scheduleNotification(notification:Notification) {
|
||||
const config = {
|
||||
id: `${notification.id}`,
|
||||
message: notification.title,
|
||||
@ -41,5 +46,3 @@ class AlarmServiceDriver {
|
||||
this.PushNotificationHandler_().localNotificationSchedule(config);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AlarmServiceDriver;
|
@ -1,11 +1,13 @@
|
||||
import PushNotificationIOS from '@react-native-community/push-notification-ios';
|
||||
import { Notification } from 'lib/models/Alarm';
|
||||
const PushNotificationIOS = require('@react-native-community/push-notification-ios').default;
|
||||
|
||||
export default class AlarmServiceDriver {
|
||||
|
||||
private hasPermission_:boolean = null;
|
||||
private inAppNotificationHandler_:any = null;
|
||||
|
||||
class AlarmServiceDriver {
|
||||
constructor() {
|
||||
this.hasPermission_ = null;
|
||||
this.inAppNotificationHandler_ = null;
|
||||
|
||||
PushNotificationIOS.addEventListener('localNotification', instance => {
|
||||
PushNotificationIOS.addEventListener('localNotification', (instance:any) => {
|
||||
if (!this.inAppNotificationHandler_) return;
|
||||
|
||||
if (!instance || !instance._data || !instance._data.id) {
|
||||
@ -26,17 +28,17 @@ class AlarmServiceDriver {
|
||||
throw new Error('Available only for non-persistent alarms');
|
||||
}
|
||||
|
||||
setInAppNotificationHandler(v) {
|
||||
setInAppNotificationHandler(v:any) {
|
||||
this.inAppNotificationHandler_ = v;
|
||||
}
|
||||
|
||||
async hasPermissions(perm = null) {
|
||||
async hasPermissions(perm:any = null) {
|
||||
if (perm !== null) return perm.alert && perm.badge && perm.sound;
|
||||
|
||||
if (this.hasPermission_ !== null) return this.hasPermission_;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
PushNotificationIOS.checkPermissions(async perm => {
|
||||
PushNotificationIOS.checkPermissions(async (perm:any) => {
|
||||
const ok = await this.hasPermissions(perm);
|
||||
this.hasPermission_ = ok;
|
||||
resolve(ok);
|
||||
@ -45,27 +47,28 @@ class AlarmServiceDriver {
|
||||
}
|
||||
|
||||
async requestPermissions() {
|
||||
const newPerm = await PushNotificationIOS.requestPermissions({
|
||||
const options:any = {
|
||||
alert: 1,
|
||||
badge: 1,
|
||||
sound: 1,
|
||||
});
|
||||
};
|
||||
const newPerm = await PushNotificationIOS.requestPermissions(options);
|
||||
this.hasPermission_ = null;
|
||||
return this.hasPermissions(newPerm);
|
||||
}
|
||||
|
||||
async clearNotification(id) {
|
||||
async clearNotification(id:any) {
|
||||
PushNotificationIOS.cancelLocalNotifications({ id: `${id}` });
|
||||
}
|
||||
|
||||
async scheduleNotification(notification) {
|
||||
async scheduleNotification(notification:Notification) {
|
||||
if (!(await this.hasPermissions())) {
|
||||
const ok = await this.requestPermissions();
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
// ID must be a string and userInfo must be supplied otherwise cancel won't work
|
||||
const iosNotification = {
|
||||
const iosNotification:any = {
|
||||
id: `${notification.id}`,
|
||||
alertTitle: notification.title,
|
||||
fireDate: notification.date.toISOString(),
|
||||
@ -77,5 +80,3 @@ class AlarmServiceDriver {
|
||||
PushNotificationIOS.scheduleLocalNotification(iosNotification);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AlarmServiceDriver;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user