mirror of https://github.com/laurent22/joplin.git
Merge branch 'dev' of https://github.com/laurent22/joplin into dev
commit
6225d20d1f
22
.eslintrc.js
22
.eslintrc.js
|
@ -130,6 +130,28 @@ module.exports = {
|
|||
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
|
||||
// make everything public which is not great. New code however should specify member accessibility.
|
||||
'@typescript-eslint/explicit-member-accessibility': ['warn'],
|
||||
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
|
||||
'@typescript-eslint/comma-dangle': ['error', {
|
||||
'arrays': 'always-multiline',
|
||||
'objects': 'always-multiline',
|
||||
'imports': 'always-multiline',
|
||||
'exports': 'always-multiline',
|
||||
'enums': 'always-multiline',
|
||||
'generics': 'always-multiline',
|
||||
'tuples': 'always-multiline',
|
||||
'functions': 'never',
|
||||
}],
|
||||
'@typescript-eslint/semi': ['error', 'always'],
|
||||
'@typescript-eslint/member-delimiter-style': ['error', {
|
||||
'multiline': {
|
||||
'delimiter': 'semi',
|
||||
'requireLast': true,
|
||||
},
|
||||
'singleline': {
|
||||
'delimiter': 'semi',
|
||||
'requireLast': false,
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
name: "\U0001F914 Feature requests and support"
|
||||
about: 'For non-bug issues we recommend using the forum, where you''ll be more likely
|
||||
to get an answer: https://discourse.joplinapp.org/'
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
If this is a feature request or a support query, please note that you'll not get an answer here.
|
||||
|
||||
Instead we recommend using the forum where you'll are a lot more likely to get an answer: https://discourse.joplinapp.org/
|
||||
|
||||
The forum is also the right place to submit a feature request so that it can be discussed by other users.
|
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "\U0001F914 Feature requests and support"
|
||||
url: https://discourse.joplinapp.org/
|
||||
about: I have a question or feature request …
|
|
@ -318,6 +318,8 @@ The currently supported template variables are:
|
|||
| `{{time}}` | Current time formatted based on the settings format | 13:00 |
|
||||
| `{{datetime}}` | Current date and time formatted based on the settings format | 01/01/19 1:00 PM |
|
||||
| `{{#custom_datetime}}` | Current date and/or time formatted based on a supplied string (using [moment.js](https://momentjs.com/) formatting) | `{{#custom_datetime}}M d{{/custom_datetime}}` |
|
||||
| `{{bowm}}` | Date of the beginning of the week (when week starts on Monday) based on the settings format | |
|
||||
| `{{bows}}` | Date of the beginning of the week (when week starts on Sunday) based on the settings format | |
|
||||
|
||||
# Searching
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ import executeSandboxCall from '@joplin/lib/services/plugins/utils/executeSandbo
|
|||
import Global from '@joplin/lib/services/plugins/api/Global';
|
||||
import mapEventHandlersToIds, { EventHandlers } from '@joplin/lib/services/plugins/utils/mapEventHandlersToIds';
|
||||
|
||||
function createConsoleWrapper(pluginId:string) {
|
||||
const wrapper:any = {};
|
||||
function createConsoleWrapper(pluginId: string) {
|
||||
const wrapper: any = {};
|
||||
|
||||
for (const n in console) {
|
||||
if (!console.hasOwnProperty(n)) continue;
|
||||
wrapper[n] = (...args:any[]) => {
|
||||
wrapper[n] = (...args: any[]) => {
|
||||
const newArgs = args.slice();
|
||||
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
|
||||
return (console as any)[n](...newArgs);
|
||||
|
@ -30,7 +30,7 @@ function createConsoleWrapper(pluginId:string) {
|
|||
|
||||
export default class PluginRunner extends BasePluginRunner {
|
||||
|
||||
private eventHandlers_:EventHandlers = {};
|
||||
private eventHandlers_: EventHandlers = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -38,13 +38,13 @@ export default class PluginRunner extends BasePluginRunner {
|
|||
this.eventHandler = this.eventHandler.bind(this);
|
||||
}
|
||||
|
||||
private async eventHandler(eventHandlerId:string, args:any[]) {
|
||||
private async eventHandler(eventHandlerId: string, args: any[]) {
|
||||
const cb = this.eventHandlers_[eventHandlerId];
|
||||
return cb(...args);
|
||||
}
|
||||
|
||||
private newSandboxProxy(pluginId:string, sandbox:Global) {
|
||||
const target = async (path:string, args:any[]) => {
|
||||
private newSandboxProxy(pluginId: string, sandbox: Global) {
|
||||
const target = async (path: string, args: any[]) => {
|
||||
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, mapEventHandlersToIds(args, this.eventHandlers_), this.eventHandler);
|
||||
};
|
||||
|
||||
|
@ -54,8 +54,8 @@ export default class PluginRunner extends BasePluginRunner {
|
|||
};
|
||||
}
|
||||
|
||||
async run(plugin:Plugin, sandbox:Global):Promise<void> {
|
||||
return new Promise((resolve:Function, reject:Function) => {
|
||||
async run(plugin: Plugin, sandbox: Global): Promise<void> {
|
||||
return new Promise((resolve: Function, reject: Function) => {
|
||||
const onStarted = () => {
|
||||
plugin.off('started', onStarted);
|
||||
resolve();
|
||||
|
|
|
@ -5,7 +5,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./tes
|
|||
const shim = require('@joplin/lib/shim').default;
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
|
||||
function newTestMdToHtml(options:any = null) {
|
||||
function newTestMdToHtml(options: any = null) {
|
||||
options = {
|
||||
ResourceModel: {
|
||||
isResourceUrl: () => false,
|
||||
|
@ -19,7 +19,7 @@ function newTestMdToHtml(options:any = null) {
|
|||
|
||||
describe('MdToHtml', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
beforeEach(async (done: Function) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
|
@ -39,7 +39,7 @@ describe('MdToHtml', function() {
|
|||
|
||||
// if (mdFilename !== 'sanitize_9.md') continue;
|
||||
|
||||
const mdToHtmlOptions:any = {
|
||||
const mdToHtmlOptions: any = {
|
||||
bodyOnly: true,
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ describe('MdToHtml', function() {
|
|||
}));
|
||||
|
||||
it('should return enabled plugin assets', asyncTest(async () => {
|
||||
const pluginOptions:any = {};
|
||||
const pluginOptions: any = {};
|
||||
const pluginNames = MdToHtml.pluginNames();
|
||||
|
||||
for (const n of pluginNames) pluginOptions[n] = { enabled: false };
|
||||
|
|
|
@ -4,7 +4,7 @@ const { expectThrow } = require('./test-utils.js');
|
|||
|
||||
// On Windows, path.resolve is going to convert a path such as
|
||||
// /tmp/file.txt to c:\tmp\file.txt
|
||||
function platformPath(path:string) {
|
||||
function platformPath(path: string) {
|
||||
if (shim.isWindows()) {
|
||||
return `c:${path.replace(/\//g, '\\')}`;
|
||||
} else {
|
||||
|
|
|
@ -4,7 +4,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('../..
|
|||
|
||||
describe('services_plugins_sandboxProxy', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
beforeEach(async (done: Function) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
|
@ -12,13 +12,13 @@ describe('services_plugins_sandboxProxy', function() {
|
|||
|
||||
it('should create a new sandbox proxy', asyncTest(async () => {
|
||||
interface Result {
|
||||
path: string,
|
||||
args: any[],
|
||||
path: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
const results:Result[] = [];
|
||||
const results: Result[] = [];
|
||||
|
||||
const target:Target = (path:string, args:any[]) => {
|
||||
const target: Target = (path: string, args: any[]) => {
|
||||
results.push({ path, args });
|
||||
};
|
||||
|
||||
|
@ -35,13 +35,13 @@ describe('services_plugins_sandboxProxy', function() {
|
|||
|
||||
it('should allow importing a namespace', asyncTest(async () => {
|
||||
interface Result {
|
||||
path: string,
|
||||
args: any[],
|
||||
path: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
const results:Result[] = [];
|
||||
const results: Result[] = [];
|
||||
|
||||
const target:Target = (path:string, args:any[]) => {
|
||||
const target: Target = (path: string, args: any[]) => {
|
||||
results.push({ path, args });
|
||||
};
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ import KeymapService from '@joplin/lib/services/KeymapService';
|
|||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
|
||||
|
||||
interface TestCommand {
|
||||
declaration: CommandDeclaration,
|
||||
runtime: CommandRuntime,
|
||||
declaration: CommandDeclaration;
|
||||
runtime: CommandRuntime;
|
||||
}
|
||||
|
||||
function newService():CommandService {
|
||||
function newService(): CommandService {
|
||||
const service = new CommandService();
|
||||
const mockStore = {
|
||||
getState: () => {
|
||||
|
@ -21,12 +21,12 @@ function newService():CommandService {
|
|||
return service;
|
||||
}
|
||||
|
||||
function createCommand(name:string, options:any):TestCommand {
|
||||
const declaration:CommandDeclaration = {
|
||||
function createCommand(name: string, options: any): TestCommand {
|
||||
const declaration: CommandDeclaration = {
|
||||
name: name,
|
||||
};
|
||||
|
||||
const runtime:CommandRuntime = {
|
||||
const runtime: CommandRuntime = {
|
||||
execute: options.execute,
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ function createCommand(name:string, options:any):TestCommand {
|
|||
return { declaration, runtime };
|
||||
}
|
||||
|
||||
function registerCommand(service:CommandService, cmd:TestCommand) {
|
||||
function registerCommand(service: CommandService, cmd: TestCommand) {
|
||||
service.registerDeclaration(cmd.declaration);
|
||||
service.registerRuntime(cmd.declaration.name, cmd.runtime);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ describe('services_CommandService', function() {
|
|||
const service = newService();
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(service);
|
||||
|
||||
const executedCommands:string[] = [];
|
||||
const executedCommands: string[] = [];
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {
|
||||
|
@ -172,9 +172,9 @@ describe('services_CommandService', function() {
|
|||
execute: () => {},
|
||||
}));
|
||||
|
||||
const clickedCommands:string[] = [];
|
||||
const clickedCommands: string[] = [];
|
||||
|
||||
const onClick = (commandName:string) => {
|
||||
const onClick = (commandName: string) => {
|
||||
clickedCommands.push(commandName);
|
||||
};
|
||||
|
||||
|
@ -234,7 +234,7 @@ describe('services_CommandService', function() {
|
|||
let propValue = null;
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: (_context:any, greeting:string) => {
|
||||
execute: (_context: any, greeting: string) => {
|
||||
propValue = greeting;
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -19,7 +19,7 @@ function exportDir() {
|
|||
return `${__dirname}/export`;
|
||||
}
|
||||
|
||||
function fieldsEqual(model1:any, model2:any, fieldNames:string[]) {
|
||||
function fieldsEqual(model1: any, model2: any, fieldNames: string[]) {
|
||||
for (let i = 0; i < fieldNames.length; i++) {
|
||||
const f = fieldNames[i];
|
||||
expect(model1[f]).toBe(model2[f], `For key ${f}`);
|
||||
|
@ -86,7 +86,7 @@ describe('services_InteropService', function() {
|
|||
await service.import({ path: filePath });
|
||||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.map((f:any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
|
||||
expect(allFolders.map((f: any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
|
||||
}));
|
||||
|
||||
it('should import folders, and only de-duplicate titles when needed', asyncTest(async () => {
|
||||
|
@ -447,14 +447,14 @@ describe('services_InteropService', function() {
|
|||
sourcePath: '',
|
||||
};
|
||||
|
||||
const module:Module = {
|
||||
const module: Module = {
|
||||
type: ModuleType.Importer,
|
||||
description: 'Test Import Module',
|
||||
format: 'testing',
|
||||
fileExtensions: ['test'],
|
||||
isCustom: true,
|
||||
|
||||
onExec: async (context:CustomImportContext) => {
|
||||
onExec: async (context: CustomImportContext) => {
|
||||
result.hasBeenExecuted = true;
|
||||
result.sourcePath = context.sourcePath;
|
||||
},
|
||||
|
@ -479,7 +479,7 @@ describe('services_InteropService', function() {
|
|||
|
||||
const filePath = `${exportDir()}/example.test`;
|
||||
|
||||
const result:any = {
|
||||
const result: any = {
|
||||
destPath: '',
|
||||
itemTypes: [],
|
||||
items: [],
|
||||
|
@ -488,28 +488,28 @@ describe('services_InteropService', function() {
|
|||
closeCalled: false,
|
||||
};
|
||||
|
||||
const module:Module = {
|
||||
const module: Module = {
|
||||
type: ModuleType.Exporter,
|
||||
description: 'Test Export Module',
|
||||
format: 'testing',
|
||||
fileExtensions: ['test'],
|
||||
isCustom: true,
|
||||
|
||||
onInit: async (context:CustomExportContext) => {
|
||||
onInit: async (context: CustomExportContext) => {
|
||||
result.destPath = context.destPath;
|
||||
},
|
||||
|
||||
onProcessItem: async (_context:CustomExportContext, itemType:number, item:any) => {
|
||||
onProcessItem: async (_context: CustomExportContext, itemType: number, item: any) => {
|
||||
result.itemTypes.push(itemType);
|
||||
result.items.push(item);
|
||||
},
|
||||
|
||||
onProcessResource: async (_context:CustomExportContext, resource:any, filePath:string) => {
|
||||
onProcessResource: async (_context: CustomExportContext, resource: any, filePath: string) => {
|
||||
result.resources.push(resource);
|
||||
result.filePaths.push(filePath);
|
||||
},
|
||||
|
||||
onClose: async (_context:CustomExportContext) => {
|
||||
onClose: async (_context: CustomExportContext) => {
|
||||
result.closeCalled = true;
|
||||
},
|
||||
};
|
||||
|
@ -524,7 +524,7 @@ describe('services_InteropService', function() {
|
|||
expect(result.destPath).toBe(filePath);
|
||||
expect(result.itemTypes.sort().join('_')).toBe('1_1_2_4');
|
||||
expect(result.items.length).toBe(4);
|
||||
expect(result.items.map((o:any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
|
||||
expect(result.items.map((o: any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
|
||||
expect(result.resources.length).toBe(1);
|
||||
expect(result.resources[0].title).toBe('photo.jpg');
|
||||
expect(result.filePaths.length).toBe(1);
|
||||
|
|
|
@ -79,7 +79,7 @@ describe('services_PluginService', function() {
|
|||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.length).toBe(2);
|
||||
expect(allFolders.map((f:any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
|
||||
expect(allFolders.map((f: any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
|
||||
}));
|
||||
|
||||
it('should load plugins from JS bundles', asyncTest(async () => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import Setting from '@joplin/lib/models/Setting';
|
|||
|
||||
const { db, asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
|
||||
function describeIfCompatible(name:string, fn:any, elseFn:any) {
|
||||
function describeIfCompatible(name: string, fn: any, elseFn: any) {
|
||||
if (['win32', 'darwin'].includes(shim.platformName())) {
|
||||
return describe(name, fn);
|
||||
} else {
|
||||
|
@ -14,14 +14,14 @@ function describeIfCompatible(name:string, fn:any, elseFn:any) {
|
|||
|
||||
describeIfCompatible('services_KeychainService', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
beforeEach(async (done: Function) => {
|
||||
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
|
||||
await switchClient(1, { keychainEnabled: true });
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done:Function) => {
|
||||
afterEach(async (done: Function) => {
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ const NoteTag = require('@joplin/lib/models/NoteTag');
|
|||
const ResourceService = require('@joplin/lib/services/ResourceService').default;
|
||||
const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine');
|
||||
|
||||
async function msleep(ms:number) {
|
||||
async function msleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
shim.setTimeout(() => {
|
||||
resolve();
|
||||
|
@ -19,7 +19,7 @@ async function msleep(ms:number) {
|
|||
});
|
||||
}
|
||||
|
||||
const createFolderForPagination = async (num:number, time:number) => {
|
||||
const createFolderForPagination = async (num: number, time: number) => {
|
||||
await Folder.save({
|
||||
title: `folder${num}`,
|
||||
updated_time: time,
|
||||
|
@ -27,7 +27,7 @@ const createFolderForPagination = async (num:number, time:number) => {
|
|||
}, { autoTimestamp: false });
|
||||
};
|
||||
|
||||
const createNoteForPagination = async (num:number, time:number) => {
|
||||
const createNoteForPagination = async (num: number, time: number) => {
|
||||
await Note.save({
|
||||
title: `note${num}`,
|
||||
body: `noteBody${num}`,
|
||||
|
@ -36,7 +36,7 @@ const createNoteForPagination = async (num:number, time:number) => {
|
|||
}, { autoTimestamp: false });
|
||||
};
|
||||
|
||||
let api:Api = null;
|
||||
let api: Api = null;
|
||||
|
||||
describe('services_rest_Api', function() {
|
||||
|
||||
|
@ -158,7 +158,7 @@ describe('services_rest_Api', function() {
|
|||
}));
|
||||
|
||||
it('should allow setting note properties', asyncTest(async () => {
|
||||
let response:any = null;
|
||||
let response: any = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
|
|
|
@ -3,7 +3,7 @@ import LockHandler, { LockType, LockHandlerOptions, Lock } from '@joplin/lib/ser
|
|||
|
||||
const { isNetworkSyncTarget, asyncTest, fileApi, setupDatabaseAndSynchronizer, synchronizer, switchClient, msleep, expectThrow, expectNotThrow } = require('./test-utils.js');
|
||||
|
||||
process.on('unhandledRejection', (reason:any, p:any) => {
|
||||
process.on('unhandledRejection', (reason: any, p: any) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
|
@ -14,13 +14,13 @@ process.on('unhandledRejection', (reason:any, p:any) => {
|
|||
// For that reason we add this multiplier for non-memory sync targets.
|
||||
const timeoutMultipler = isNetworkSyncTarget() ? 100 : 1;
|
||||
|
||||
let lockHandler_:LockHandler = null;
|
||||
let lockHandler_: LockHandler = null;
|
||||
|
||||
function newLockHandler(options:LockHandlerOptions = null):LockHandler {
|
||||
function newLockHandler(options: LockHandlerOptions = null): LockHandler {
|
||||
return new LockHandler(fileApi(), options);
|
||||
}
|
||||
|
||||
function lockHandler():LockHandler {
|
||||
function lockHandler(): LockHandler {
|
||||
if (lockHandler_) return lockHandler_;
|
||||
lockHandler_ = new LockHandler(fileApi());
|
||||
return lockHandler_;
|
||||
|
@ -28,7 +28,7 @@ function lockHandler():LockHandler {
|
|||
|
||||
describe('synchronizer_LockHandler', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
beforeEach(async (done: Function) => {
|
||||
// logger.setLevel(Logger.LEVEL_WARN);
|
||||
lockHandler_ = null;
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
|
@ -96,8 +96,8 @@ describe('synchronizer_LockHandler', function() {
|
|||
});
|
||||
|
||||
const lock = await handler.acquireLock(LockType.Sync, 'desktop', '111');
|
||||
let autoLockError:any = null;
|
||||
handler.startAutoLockRefresh(lock, (error:any) => {
|
||||
let autoLockError: any = null;
|
||||
handler.startAutoLockRefresh(lock, (error: any) => {
|
||||
autoLockError = error;
|
||||
});
|
||||
|
||||
|
@ -144,8 +144,8 @@ describe('synchronizer_LockHandler', function() {
|
|||
const lockHandler = newLockHandler();
|
||||
|
||||
{
|
||||
const lock1:Lock = { type: LockType.Exclusive, clientId: '1', clientType: 'd' };
|
||||
const lock2:Lock = { type: LockType.Exclusive, clientId: '2', clientType: 'd' };
|
||||
const lock1: Lock = { type: LockType.Exclusive, clientId: '1', clientType: 'd' };
|
||||
const lock2: Lock = { type: LockType.Exclusive, clientId: '2', clientType: 'd' };
|
||||
await lockHandler.saveLock_(lock1);
|
||||
await msleep(100);
|
||||
await lockHandler.saveLock_(lock2);
|
||||
|
|
|
@ -15,43 +15,43 @@ const MasterKey = require('@joplin/lib/models/MasterKey');
|
|||
|
||||
const specTimeout = 60000 * 10; // Nextcloud tests can be slow
|
||||
|
||||
let lockHandler_:LockHandler = null;
|
||||
let migrationHandler_:MigrationHandler = null;
|
||||
let lockHandler_: LockHandler = null;
|
||||
let migrationHandler_: MigrationHandler = null;
|
||||
|
||||
function lockHandler():LockHandler {
|
||||
function lockHandler(): LockHandler {
|
||||
if (lockHandler_) return lockHandler_;
|
||||
lockHandler_ = new LockHandler(fileApi());
|
||||
return lockHandler_;
|
||||
}
|
||||
|
||||
function migrationHandler(clientId:string = 'abcd'):MigrationHandler {
|
||||
function migrationHandler(clientId: string = 'abcd'): MigrationHandler {
|
||||
if (migrationHandler_) return migrationHandler_;
|
||||
migrationHandler_ = new MigrationHandler(fileApi(), lockHandler(), 'desktop', clientId);
|
||||
return migrationHandler_;
|
||||
}
|
||||
|
||||
interface MigrationTests {
|
||||
[key:string]: Function;
|
||||
[key: string]: Function;
|
||||
}
|
||||
|
||||
const migrationTests:MigrationTests = {
|
||||
const migrationTests: MigrationTests = {
|
||||
2: async function() {
|
||||
const items = (await fileApi().list('', { includeHidden: true })).items;
|
||||
expect(items.filter((i:any) => i.path === '.resource' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i:any) => i.path === 'locks' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i:any) => i.path === 'temp' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i:any) => i.path === 'info.json' && !i.isDir).length).toBe(1);
|
||||
expect(items.filter((i: any) => i.path === '.resource' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i: any) => i.path === 'locks' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i: any) => i.path === 'temp' && i.isDir).length).toBe(1);
|
||||
expect(items.filter((i: any) => i.path === 'info.json' && !i.isDir).length).toBe(1);
|
||||
|
||||
const versionForOldClients = await fileApi().get('.sync/version.txt');
|
||||
expect(versionForOldClients).toBe('2');
|
||||
},
|
||||
};
|
||||
|
||||
let previousSyncTargetName:string = '';
|
||||
let previousSyncTargetName: string = '';
|
||||
|
||||
describe('synchronizer_MigrationHandler', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
beforeEach(async (done: Function) => {
|
||||
// To test the migrations, we have to use the filesystem sync target
|
||||
// because the sync target snapshots are plain files. Eventually
|
||||
// it should be possible to copy a filesystem target to memory
|
||||
|
@ -65,7 +65,7 @@ describe('synchronizer_MigrationHandler', function() {
|
|||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done:Function) => {
|
||||
afterEach(async (done: Function) => {
|
||||
setSyncTargetName(previousSyncTargetName);
|
||||
done();
|
||||
});
|
||||
|
@ -74,8 +74,8 @@ describe('synchronizer_MigrationHandler', function() {
|
|||
// Check that basic folders "locks" and "temp" are created for new sync targets.
|
||||
await migrationHandler().upgrade(1);
|
||||
const result = await fileApi().list();
|
||||
expect(result.items.filter((i:any) => i.path === Dirnames.Locks).length).toBe(1);
|
||||
expect(result.items.filter((i:any) => i.path === Dirnames.Temp).length).toBe(1);
|
||||
expect(result.items.filter((i: any) => i.path === Dirnames.Locks).length).toBe(1);
|
||||
expect(result.items.filter((i: any) => i.path === Dirnames.Temp).length).toBe(1);
|
||||
}), specTimeout);
|
||||
|
||||
it('should not allow syncing if the sync target is out-dated', asyncTest(async () => {
|
||||
|
|
|
@ -10,28 +10,28 @@ const fs = require('fs-extra');
|
|||
const { ipcMain } = require('electron');
|
||||
|
||||
interface RendererProcessQuitReply {
|
||||
canClose: boolean,
|
||||
canClose: boolean;
|
||||
}
|
||||
|
||||
interface PluginWindows {
|
||||
[key: string]: any,
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default class ElectronAppWrapper {
|
||||
|
||||
private logger_:Logger = null;
|
||||
private electronApp_:any;
|
||||
private env_:string;
|
||||
private isDebugMode_:boolean;
|
||||
private profilePath_:string;
|
||||
private win_:any = null;
|
||||
private willQuitApp_:boolean = false;
|
||||
private tray_:any = null;
|
||||
private buildDir_:string = null;
|
||||
private rendererProcessQuitReply_:RendererProcessQuitReply = null;
|
||||
private pluginWindows_:PluginWindows = {};
|
||||
private logger_: Logger = null;
|
||||
private electronApp_: any;
|
||||
private env_: string;
|
||||
private isDebugMode_: boolean;
|
||||
private profilePath_: string;
|
||||
private win_: any = null;
|
||||
private willQuitApp_: boolean = false;
|
||||
private tray_: any = null;
|
||||
private buildDir_: string = null;
|
||||
private rendererProcessQuitReply_: RendererProcessQuitReply = null;
|
||||
private pluginWindows_: PluginWindows = {};
|
||||
|
||||
constructor(electronApp:any, env:string, profilePath:string, isDebugMode:boolean) {
|
||||
constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean) {
|
||||
this.electronApp_ = electronApp;
|
||||
this.env_ = env;
|
||||
this.isDebugMode_ = isDebugMode;
|
||||
|
@ -42,7 +42,7 @@ export default class ElectronAppWrapper {
|
|||
return this.electronApp_;
|
||||
}
|
||||
|
||||
setLogger(v:Logger) {
|
||||
setLogger(v: Logger) {
|
||||
this.logger_ = v;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ export default class ElectronAppWrapper {
|
|||
const windowStateKeeper = require('electron-window-state');
|
||||
|
||||
|
||||
const stateOptions:any = {
|
||||
const stateOptions: any = {
|
||||
defaultWidth: Math.round(0.8 * screen.getPrimaryDisplay().workArea.width),
|
||||
defaultHeight: Math.round(0.8 * screen.getPrimaryDisplay().workArea.height),
|
||||
file: `window-state-${this.env_}.json`,
|
||||
|
@ -76,7 +76,7 @@ export default class ElectronAppWrapper {
|
|||
// Load the previous state with fallback to defaults
|
||||
const windowState = windowStateKeeper(stateOptions);
|
||||
|
||||
const windowOptions:any = {
|
||||
const windowOptions: any = {
|
||||
x: windowState.x,
|
||||
y: windowState.y,
|
||||
width: windowState.width,
|
||||
|
@ -128,7 +128,7 @@ export default class ElectronAppWrapper {
|
|||
}, 3000);
|
||||
}
|
||||
|
||||
this.win_.on('close', (event:any) => {
|
||||
this.win_.on('close', (event: any) => {
|
||||
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
|
||||
// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
|
||||
// user clicks on the icon in the task bar).
|
||||
|
@ -176,7 +176,7 @@ export default class ElectronAppWrapper {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.on('asynchronous-message', (_event:any, message:string, args:any) => {
|
||||
ipcMain.on('asynchronous-message', (_event: any, message: string, args: any) => {
|
||||
if (message === 'appCloseReply') {
|
||||
// We got the response from the renderer process:
|
||||
// save the response and try quit again.
|
||||
|
@ -187,7 +187,7 @@ export default class ElectronAppWrapper {
|
|||
|
||||
// This handler receives IPC messages from a plugin or from the main window,
|
||||
// and forwards it to the main window or the plugin window.
|
||||
ipcMain.on('pluginMessage', (_event:any, message:PluginMessage) => {
|
||||
ipcMain.on('pluginMessage', (_event: any, message: PluginMessage) => {
|
||||
if (message.target === 'mainWindow') {
|
||||
this.win_.webContents.send('pluginMessage', message);
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ export default class ElectronAppWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
registerPluginWindow(pluginId:string, window:any) {
|
||||
registerPluginWindow(pluginId: string, window: any) {
|
||||
this.pluginWindows_[pluginId] = window;
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ export default class ElectronAppWrapper {
|
|||
}
|
||||
|
||||
// Note: this must be called only after the "ready" event of the app has been dispatched
|
||||
createTray(contextMenu:any) {
|
||||
createTray(contextMenu: any) {
|
||||
try {
|
||||
this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`);
|
||||
this.tray_.setToolTip(this.electronApp_.name);
|
||||
|
|
|
@ -13,20 +13,20 @@ const md5 = require('md5');
|
|||
const url = require('url');
|
||||
|
||||
interface ExportNoteOptions {
|
||||
customCss?: string,
|
||||
sourceNoteIds?: string[],
|
||||
sourceFolderIds?: string[],
|
||||
printBackground?: boolean,
|
||||
pageSize?: string,
|
||||
landscape?: boolean,
|
||||
customCss?: string;
|
||||
sourceNoteIds?: string[];
|
||||
sourceFolderIds?: string[];
|
||||
printBackground?: boolean;
|
||||
pageSize?: string;
|
||||
landscape?: boolean;
|
||||
}
|
||||
|
||||
export default class InteropServiceHelper {
|
||||
|
||||
private static async exportNoteToHtmlFile(noteId:string, exportOptions:ExportNoteOptions) {
|
||||
private static async exportNoteToHtmlFile(noteId: string, exportOptions: ExportNoteOptions) {
|
||||
const tempFile = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.html`;
|
||||
|
||||
const fullExportOptions:ExportOptions = Object.assign({}, {
|
||||
const fullExportOptions: ExportOptions = Object.assign({}, {
|
||||
path: tempFile,
|
||||
format: 'html',
|
||||
target: FileSystemItem.File,
|
||||
|
@ -41,9 +41,9 @@ export default class InteropServiceHelper {
|
|||
return tempFile;
|
||||
}
|
||||
|
||||
private static async exportNoteTo_(target:string, noteId:string, options:ExportNoteOptions = {}) {
|
||||
let win:any = null;
|
||||
let htmlFile:string = null;
|
||||
private static async exportNoteTo_(target: string, noteId: string, options: ExportNoteOptions = {}) {
|
||||
let win: any = null;
|
||||
let htmlFile: string = null;
|
||||
|
||||
const cleanup = () => {
|
||||
if (win) win.destroy();
|
||||
|
@ -87,7 +87,7 @@ export default class InteropServiceHelper {
|
|||
// Maybe can be fixed by doing everything from main process?
|
||||
// i.e. creating a function `print()` that takes the `htmlFile` variable as input.
|
||||
|
||||
win.webContents.print(options, (success:boolean, reason:string) => {
|
||||
win.webContents.print(options, (success: boolean, reason: string) => {
|
||||
// TODO: This is correct but broken in Electron 4. Need to upgrade to 5+
|
||||
// It calls the callback right away with "false" even if the document hasn't be print yet.
|
||||
|
||||
|
@ -112,15 +112,15 @@ export default class InteropServiceHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static async exportNoteToPdf(noteId:string, options:ExportNoteOptions = {}) {
|
||||
public static async exportNoteToPdf(noteId: string, options: ExportNoteOptions = {}) {
|
||||
return this.exportNoteTo_('pdf', noteId, options);
|
||||
}
|
||||
|
||||
public static async printNote(noteId:string, options:ExportNoteOptions = {}) {
|
||||
public static async printNote(noteId: string, options: ExportNoteOptions = {}) {
|
||||
return this.exportNoteTo_('printer', noteId, options);
|
||||
}
|
||||
|
||||
public static async defaultFilename(noteId:string, fileExtension:string) {
|
||||
public static async defaultFilename(noteId: string, fileExtension: string) {
|
||||
// Default filename is just the date
|
||||
const date = time.formatMsToLocal(new Date().getTime(), time.dateFormat());
|
||||
let filename = friendlySafeFilename(`${date}`, 100);
|
||||
|
@ -134,7 +134,7 @@ export default class InteropServiceHelper {
|
|||
return `${filename}.${fileExtension}`;
|
||||
}
|
||||
|
||||
public static async export(_dispatch:Function, module:Module, options:ExportNoteOptions = null) {
|
||||
public static async export(_dispatch: Function, module: Module, options: ExportNoteOptions = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
let path = null;
|
||||
|
@ -157,7 +157,7 @@ export default class InteropServiceHelper {
|
|||
|
||||
CommandService.instance().execute('showModalMessage', _('Exporting to "%s" as "%s" format. Please wait...', path, module.format));
|
||||
|
||||
const exportOptions:ExportOptions = {};
|
||||
const exportOptions: ExportOptions = {};
|
||||
exportOptions.path = path;
|
||||
exportOptions.format = module.format;
|
||||
exportOptions.modulePath = module.path;
|
||||
|
|
|
@ -96,29 +96,29 @@ const pluginClasses = [
|
|||
];
|
||||
|
||||
interface AppStateRoute {
|
||||
type: string,
|
||||
routeName: string,
|
||||
props: any,
|
||||
type: string;
|
||||
routeName: string;
|
||||
props: any;
|
||||
}
|
||||
|
||||
export interface AppState extends State {
|
||||
route: AppStateRoute,
|
||||
navHistory: any[],
|
||||
noteVisiblePanes: string[],
|
||||
sidebarVisibility: boolean,
|
||||
noteListVisibility: boolean,
|
||||
windowContentSize: any,
|
||||
watchedNoteFiles: string[],
|
||||
lastEditorScrollPercents: any,
|
||||
devToolsVisible: boolean,
|
||||
visibleDialogs: any, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
||||
focusedField: string,
|
||||
route: AppStateRoute;
|
||||
navHistory: any[];
|
||||
noteVisiblePanes: string[];
|
||||
sidebarVisibility: boolean;
|
||||
noteListVisibility: boolean;
|
||||
windowContentSize: any;
|
||||
watchedNoteFiles: string[];
|
||||
lastEditorScrollPercents: any;
|
||||
devToolsVisible: boolean;
|
||||
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
||||
focusedField: string;
|
||||
|
||||
// Extra reducer keys go here
|
||||
watchedResources: any,
|
||||
watchedResources: any;
|
||||
}
|
||||
|
||||
const appDefaultState:AppState = {
|
||||
const appDefaultState: AppState = {
|
||||
...defaultState,
|
||||
route: {
|
||||
type: 'NAV_GO',
|
||||
|
@ -154,7 +154,7 @@ class Application extends BaseApplication {
|
|||
return `${Setting.value('profileDir')}/log-autoupdater.txt`;
|
||||
}
|
||||
|
||||
reducer(state:AppState = appDefaultState, action:any) {
|
||||
reducer(state: AppState = appDefaultState, action: any) {
|
||||
let newState = state;
|
||||
|
||||
try {
|
||||
|
@ -200,7 +200,7 @@ class Application extends BaseApplication {
|
|||
case 'NOTE_VISIBLE_PANES_TOGGLE':
|
||||
|
||||
{
|
||||
const getNextLayout = (currentLayout:any) => {
|
||||
const getNextLayout = (currentLayout: any) => {
|
||||
currentLayout = panes.length === 2 ? 'both' : currentLayout[0];
|
||||
|
||||
let paneOptions;
|
||||
|
@ -345,7 +345,7 @@ class Application extends BaseApplication {
|
|||
return newState;
|
||||
}
|
||||
|
||||
toggleDevTools(visible:boolean) {
|
||||
toggleDevTools(visible: boolean) {
|
||||
if (visible) {
|
||||
bridge().openDevTools();
|
||||
} else {
|
||||
|
@ -353,7 +353,7 @@ class Application extends BaseApplication {
|
|||
}
|
||||
}
|
||||
|
||||
async generalMiddleware(store:any, next:any, action:any) {
|
||||
async generalMiddleware(store: any, next: any, action: any) {
|
||||
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'locale' || action.type == 'SETTING_UPDATE_ALL') {
|
||||
setLocale(Setting.value('locale'));
|
||||
// The bridge runs within the main process, with its own instance of locale.js
|
||||
|
@ -459,14 +459,14 @@ class Application extends BaseApplication {
|
|||
// The context menu must be setup in renderer process because that's where
|
||||
// the spell checker service lives.
|
||||
require('electron-context-menu')({
|
||||
shouldShowMenu: (_event:any, params:any) => {
|
||||
shouldShowMenu: (_event: any, params: any) => {
|
||||
// params.inputFieldType === 'none' when right-clicking the text editor. This is a bit of a hack to detect it because in this
|
||||
// case we don't want to use the built-in context menu but a custom one.
|
||||
return params.isEditable && params.inputFieldType !== 'none';
|
||||
},
|
||||
|
||||
menu: (actions:any, props:any) => {
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(props.misspelledWord, props.dictionarySuggestions).map((item:any) => new MenuItem(item));
|
||||
menu: (actions: any, props: any) => {
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(props.misspelledWord, props.dictionarySuggestions).map((item: any) => new MenuItem(item));
|
||||
|
||||
const output = [
|
||||
actions.cut(),
|
||||
|
@ -480,7 +480,7 @@ class Application extends BaseApplication {
|
|||
});
|
||||
}
|
||||
|
||||
async loadCustomCss(filePath:string) {
|
||||
async loadCustomCss(filePath: string) {
|
||||
let cssString = '';
|
||||
if (await fs.pathExists(filePath)) {
|
||||
try {
|
||||
|
@ -497,7 +497,7 @@ class Application extends BaseApplication {
|
|||
return cssString;
|
||||
}
|
||||
|
||||
async start(argv:string[]):Promise<any> {
|
||||
async start(argv: string[]): Promise<any> {
|
||||
const electronIsDev = require('electron-is-dev');
|
||||
|
||||
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
|
||||
|
@ -527,7 +527,7 @@ class Application extends BaseApplication {
|
|||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||
AlarmService.setLogger(reg.logger());
|
||||
|
||||
reg.setShowErrorMessageBoxHandler((message:string) => { bridge().showErrorMessageBox(message); });
|
||||
reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); });
|
||||
|
||||
if (Setting.value('flagOpenDevTools')) {
|
||||
bridge().openDevTools();
|
||||
|
@ -672,7 +672,7 @@ class Application extends BaseApplication {
|
|||
ExternalEditWatcher.instance().setLogger(reg.logger());
|
||||
ExternalEditWatcher.instance().dispatch = this.store().dispatch;
|
||||
|
||||
ResourceEditWatcher.instance().initialize(reg.logger(), (action:any) => { this.store().dispatch(action); });
|
||||
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); });
|
||||
|
||||
RevisionService.instance().runInBackground();
|
||||
|
||||
|
@ -705,7 +705,7 @@ class Application extends BaseApplication {
|
|||
|
||||
try {
|
||||
if (Setting.value('plugins.devPluginPaths')) {
|
||||
const paths = Setting.value('plugins.devPluginPaths').split(',').map((p:string) => p.trim());
|
||||
const paths = Setting.value('plugins.devPluginPaths').split(',').map((p: string) => p.trim());
|
||||
await PluginService.instance().loadAndRunPlugins(paths);
|
||||
}
|
||||
|
||||
|
@ -732,7 +732,7 @@ class Application extends BaseApplication {
|
|||
|
||||
}
|
||||
|
||||
let application_:Application = null;
|
||||
let application_: Application = null;
|
||||
|
||||
function app() {
|
||||
if (!application_) application_ = new Application();
|
||||
|
|
|
@ -6,20 +6,20 @@ const { dirname, toSystemSlashes } = require('@joplin/lib/path-utils');
|
|||
const { BrowserWindow, nativeTheme } = require('electron');
|
||||
|
||||
interface LastSelectedPath {
|
||||
file: string,
|
||||
directory: string,
|
||||
file: string;
|
||||
directory: string;
|
||||
}
|
||||
|
||||
interface LastSelectedPaths {
|
||||
[key:string]: LastSelectedPath,
|
||||
[key: string]: LastSelectedPath;
|
||||
}
|
||||
|
||||
export class Bridge {
|
||||
|
||||
private electronWrapper_:ElectronAppWrapper;
|
||||
private lastSelectedPaths_:LastSelectedPaths;
|
||||
private electronWrapper_: ElectronAppWrapper;
|
||||
private lastSelectedPaths_: LastSelectedPaths;
|
||||
|
||||
constructor(electronWrapper:ElectronAppWrapper) {
|
||||
constructor(electronWrapper: ElectronAppWrapper) {
|
||||
this.electronWrapper_ = electronWrapper;
|
||||
this.lastSelectedPaths_ = {
|
||||
file: null,
|
||||
|
@ -43,11 +43,11 @@ export class Bridge {
|
|||
return this.electronWrapper_.window();
|
||||
}
|
||||
|
||||
showItemInFolder(fullPath:string) {
|
||||
showItemInFolder(fullPath: string) {
|
||||
return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath));
|
||||
}
|
||||
|
||||
newBrowserWindow(options:any) {
|
||||
newBrowserWindow(options: any) {
|
||||
return new BrowserWindow(options);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ export class Bridge {
|
|||
return { width: s[0], height: s[1] };
|
||||
}
|
||||
|
||||
windowSetSize(width:number, height:number) {
|
||||
windowSetSize(width: number, height: number) {
|
||||
if (!this.window()) return;
|
||||
return this.window().setSize(width, height);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ export class Bridge {
|
|||
return this.window().webContents.closeDevTools();
|
||||
}
|
||||
|
||||
showSaveDialog(options:any) {
|
||||
showSaveDialog(options: any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file;
|
||||
|
@ -87,7 +87,7 @@ export class Bridge {
|
|||
return filePath;
|
||||
}
|
||||
|
||||
showOpenDialog(options:any) {
|
||||
showOpenDialog(options: any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
let fileType = 'file';
|
||||
|
@ -102,13 +102,13 @@ export class Bridge {
|
|||
}
|
||||
|
||||
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
|
||||
showMessageBox_(window:any, options:any):number {
|
||||
showMessageBox_(window: any, options: any): number {
|
||||
const { dialog } = require('electron');
|
||||
if (!window) window = this.window();
|
||||
return dialog.showMessageBoxSync(window, options);
|
||||
}
|
||||
|
||||
showErrorMessageBox(message:string) {
|
||||
showErrorMessageBox(message: string) {
|
||||
return this.showMessageBox_(this.window(), {
|
||||
type: 'error',
|
||||
message: message,
|
||||
|
@ -116,7 +116,7 @@ export class Bridge {
|
|||
});
|
||||
}
|
||||
|
||||
showConfirmMessageBox(message:string, options:any = null) {
|
||||
showConfirmMessageBox(message: string, options: any = null) {
|
||||
if (options === null) options = {};
|
||||
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
|
@ -130,7 +130,7 @@ export class Bridge {
|
|||
}
|
||||
|
||||
/* returns the index of the clicked button */
|
||||
showMessageBox(message:string, options:any = null) {
|
||||
showMessageBox(message: string, options: any = null) {
|
||||
if (options === null) options = {};
|
||||
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
|
@ -142,7 +142,7 @@ export class Bridge {
|
|||
return result;
|
||||
}
|
||||
|
||||
showInfoMessageBox(message:string, options:any = {}) {
|
||||
showInfoMessageBox(message: string, options: any = {}) {
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
type: 'info',
|
||||
message: message,
|
||||
|
@ -151,7 +151,7 @@ export class Bridge {
|
|||
return result === 0;
|
||||
}
|
||||
|
||||
setLocale(locale:string) {
|
||||
setLocale(locale: string) {
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
|
@ -163,15 +163,15 @@ export class Bridge {
|
|||
return require('electron').MenuItem;
|
||||
}
|
||||
|
||||
openExternal(url:string) {
|
||||
openExternal(url: string) {
|
||||
return require('electron').shell.openExternal(url);
|
||||
}
|
||||
|
||||
openItem(fullPath:string) {
|
||||
openItem(fullPath: string) {
|
||||
return require('electron').shell.openItem(fullPath);
|
||||
}
|
||||
|
||||
checkForUpdates(inBackground:boolean, window:any, logFilePath:string, options:any) {
|
||||
checkForUpdates(inBackground: boolean, window: any, logFilePath: string, options: any) {
|
||||
const { checkForUpdates } = require('./checkForUpdates.js');
|
||||
checkForUpdates(inBackground, window, logFilePath, options);
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ export class Bridge {
|
|||
return nativeTheme.shouldUseDarkColors;
|
||||
}
|
||||
|
||||
addEventListener(name:string, fn:Function) {
|
||||
addEventListener(name: string, fn: Function) {
|
||||
if (name === 'nativeThemeUpdated') {
|
||||
nativeTheme.on('updated', fn);
|
||||
} else {
|
||||
|
@ -218,9 +218,9 @@ export class Bridge {
|
|||
|
||||
}
|
||||
|
||||
let bridge_:Bridge = null;
|
||||
let bridge_: Bridge = null;
|
||||
|
||||
export function initBridge(wrapper:ElectronAppWrapper) {
|
||||
export function initBridge(wrapper: ElectronAppWrapper) {
|
||||
if (bridge_) throw new Error('Bridge already initialized');
|
||||
bridge_ = new Bridge(wrapper);
|
||||
return bridge_;
|
||||
|
|
|
@ -3,12 +3,12 @@ import { _ } from '@joplin/lib/locale';
|
|||
const app = require('electron').remote.app;
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'copyDevCommand',
|
||||
label: () => _('Copy dev mode command to clipboard'),
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
const appPath = app.getPath('exe');
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import CommandService, { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElement',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:any, target:string) => {
|
||||
execute: async (_context: any, target: string) => {
|
||||
if (target === 'noteBody') return CommandService.instance().execute('focusElementNoteBody');
|
||||
if (target === 'noteList') return CommandService.instance().execute('focusElementNoteList');
|
||||
if (target === 'sideBar') return CommandService.instance().execute('focusElementSideBar');
|
||||
|
|
|
@ -3,12 +3,12 @@ import { _ } from '@joplin/lib/locale';
|
|||
import bridge from '../services/bridge';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openProfileDirectory',
|
||||
label: () => _('Open profile directory'),
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
bridge().openItem(Setting.value('profileDir'));
|
||||
|
|
|
@ -5,15 +5,15 @@ const Note = require('@joplin/lib/models/Note');
|
|||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'startExternalEditing',
|
||||
label: () => _('Edit in external editor'),
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
try {
|
||||
|
|
|
@ -3,15 +3,15 @@ import { _ } from '@joplin/lib/locale';
|
|||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'stopExternalEditing',
|
||||
label: () => _('Stop external editing'),
|
||||
iconName: 'fa-stop',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
ExternalEditWatcher.instance().stopWatching(noteId);
|
||||
},
|
||||
|
|
|
@ -3,15 +3,15 @@ import { _ } from '@joplin/lib/locale';
|
|||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { DesktopCommandContext } from '../services/commands/types';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleExternalEditing',
|
||||
label: () => _('Toggle external editing'),
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:DesktopCommandContext, noteId:string = null) => {
|
||||
execute: async (context: DesktopCommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
if (!noteId) return;
|
||||
|
@ -23,7 +23,7 @@ export const runtime = ():CommandRuntime => {
|
|||
}
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
mapStateToTitle: (state:any) => {
|
||||
mapStateToTitle: (state: any) => {
|
||||
const noteId = stateUtils.selectedNoteId(state);
|
||||
return state.watchedNoteFiles.includes(noteId) ? _('Stop') : '';
|
||||
},
|
||||
|
|
|
@ -10,16 +10,16 @@ export enum ButtonLevel {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
title?: string,
|
||||
iconName?: string,
|
||||
level?: ButtonLevel,
|
||||
className?:string,
|
||||
onClick:Function,
|
||||
color?: string,
|
||||
iconAnimation?: string,
|
||||
tooltip?: string,
|
||||
disabled?: boolean,
|
||||
style?:any,
|
||||
title?: string;
|
||||
iconName?: string;
|
||||
level?: ButtonLevel;
|
||||
className?: string;
|
||||
onClick: Function;
|
||||
color?: string;
|
||||
iconAnimation?: string;
|
||||
tooltip?: string;
|
||||
disabled?: boolean;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
const StyledTitle = styled.span`
|
||||
|
@ -30,143 +30,143 @@ const StyledButtonBase = styled.button`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
min-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
max-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
width: ${(props:any) => props.iconOnly ? `${props.theme.toolbarHeight}px` : 'auto'};
|
||||
${(props:any) => props.iconOnly ? `min-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
${(props:any) => !props.iconOnly ? 'min-width: 100px;' : ''}
|
||||
${(props:any) => props.iconOnly ? `max-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
height: ${(props: any) => `${props.theme.toolbarHeight}px`};
|
||||
min-height: ${(props: any) => `${props.theme.toolbarHeight}px`};
|
||||
max-height: ${(props: any) => `${props.theme.toolbarHeight}px`};
|
||||
width: ${(props: any) => props.iconOnly ? `${props.theme.toolbarHeight}px` : 'auto'};
|
||||
${(props: any) => props.iconOnly ? `min-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
${(props: any) => !props.iconOnly ? 'min-width: 100px;' : ''}
|
||||
${(props: any) => props.iconOnly ? `max-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
font-size: ${(props:any) => props.theme.fontSize}px;
|
||||
padding: 0 ${(props:any) => props.iconOnly ? 4 : 8}px;
|
||||
font-size: ${(props: any) => props.theme.fontSize}px;
|
||||
padding: 0 ${(props: any) => props.iconOnly ? 4 : 8}px;
|
||||
justify-content: center;
|
||||
opacity: ${(props:any) => props.disabled ? 0.5 : 1};
|
||||
opacity: ${(props: any) => props.disabled ? 0.5 : 1};
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(styled.span(space))`
|
||||
font-size: ${(props:any) => props.theme.toolbarIconSize}px;
|
||||
${(props:any) => props.animation ? `animation: ${props.animation}` : ''};
|
||||
font-size: ${(props: any) => props.theme.toolbarIconSize}px;
|
||||
${(props: any) => props.animation ? `animation: ${props.animation}` : ''};
|
||||
`;
|
||||
|
||||
const StyledButtonPrimary = styled(StyledButtonBase)`
|
||||
border: none;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor5};
|
||||
background-color: ${(props: any) => props.theme.backgroundColor5};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover5};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorHover5};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive5};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorActive5};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color5};
|
||||
color: ${(props: any) => props.theme.color5};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color5};
|
||||
color: ${(props: any) => props.theme.color5};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonSecondary = styled(StyledButtonBase)`
|
||||
border: 1px solid ${(props:any) => props.theme.borderColor4};
|
||||
background-color: ${(props:any) => props.theme.backgroundColor4};
|
||||
border: 1px solid ${(props: any) => props.theme.borderColor4};
|
||||
background-color: ${(props: any) => props.theme.backgroundColor4};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover4};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorHover4};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive4};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorActive4};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color4};
|
||||
color: ${(props: any) => props.theme.color4};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color4};
|
||||
color: ${(props: any) => props.theme.color4};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonTertiary = styled(StyledButtonBase)`
|
||||
border: 1px solid ${(props:any) => props.theme.color3};
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
border: 1px solid ${(props: any) => props.theme.color3};
|
||||
background-color: ${(props: any) => props.theme.backgroundColor3};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHoverDim3};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorHoverDim3};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive3};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorActive3};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color};
|
||||
color: ${(props: any) => props.theme.color};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color};
|
||||
color: ${(props: any) => props.theme.color};
|
||||
opacity: 0.9;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonSideBarSecondary = styled(StyledButtonBase)`
|
||||
background: none;
|
||||
border-color: ${(props:any) => props.theme.color2};
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
border-color: ${(props: any) => props.theme.color2};
|
||||
color: ${(props: any) => props.theme.color2};
|
||||
|
||||
&:hover {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
border-color: ${(props:any) => props.theme.colorHover2};
|
||||
color: ${(props: any) => props.theme.colorHover2};
|
||||
border-color: ${(props: any) => props.theme.colorHover2};
|
||||
background: none;
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
color: ${(props: any) => props.theme.colorHover2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
color: ${(props: any) => props.theme.colorHover2};
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
border-color: ${(props:any) => props.theme.colorActive2};
|
||||
color: ${(props: any) => props.theme.colorActive2};
|
||||
border-color: ${(props: any) => props.theme.colorActive2};
|
||||
background: none;
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
color: ${(props: any) => props.theme.colorActive2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
color: ${(props: any) => props.theme.colorActive2};
|
||||
}
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
color: ${(props: any) => props.theme.color2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
color: ${(props: any) => props.theme.color2};
|
||||
}
|
||||
`;
|
||||
|
||||
function buttonClass(level:ButtonLevel) {
|
||||
function buttonClass(level: ButtonLevel) {
|
||||
if (level === ButtonLevel.Primary) return StyledButtonPrimary;
|
||||
if (level === ButtonLevel.Tertiary) return StyledButtonTertiary;
|
||||
if (level === ButtonLevel.SideBarSecondary) return StyledButtonSideBarSecondary;
|
||||
return StyledButtonSecondary;
|
||||
}
|
||||
|
||||
export default function Button(props:Props) {
|
||||
export default function Button(props: Props) {
|
||||
const iconOnly = props.iconName && !props.title;
|
||||
|
||||
const StyledButton = buttonClass(props.level);
|
||||
|
|
|
@ -4,25 +4,25 @@ import { _ } from '@joplin/lib/locale';
|
|||
const styled = require('styled-components').default;
|
||||
|
||||
interface Props {
|
||||
backButtonTitle?: string,
|
||||
hasChanges?: boolean,
|
||||
onCancelClick: Function,
|
||||
onSaveClick?: Function,
|
||||
onApplyClick?: Function,
|
||||
backButtonTitle?: string;
|
||||
hasChanges?: boolean;
|
||||
onCancelClick: Function;
|
||||
onSaveClick?: Function;
|
||||
onApplyClick?: Function;
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
padding-left: ${(props:any) => props.theme.configScreenPadding}px;
|
||||
background-color: ${(props: any) => props.theme.backgroundColor3};
|
||||
padding-left: ${(props: any) => props.theme.configScreenPadding}px;
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: ${(props:any) => props.theme.dividerColor};
|
||||
border-top-color: ${(props: any) => props.theme.dividerColor};
|
||||
`;
|
||||
|
||||
export default function ButtonBar(props:Props) {
|
||||
export default function ButtonBar(props: Props) {
|
||||
function renderOkButton() {
|
||||
if (!props.onSaveClick) return null;
|
||||
return <Button style={{ marginRight: 10 }} level={ButtonLevel.Primary} disabled={!props.hasChanges} onClick={props.onSaveClick} title={_('OK')}/>;
|
||||
|
|
|
@ -16,9 +16,9 @@ const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
|||
|
||||
class ConfigScreenComponent extends React.Component<any, any> {
|
||||
|
||||
rowStyle_:any = null;
|
||||
rowStyle_: any = null;
|
||||
|
||||
constructor(props:any) {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
shared.init(this);
|
||||
|
@ -72,7 +72,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
}
|
||||
|
||||
sectionByName(name:string) {
|
||||
sectionByName(name: string) {
|
||||
const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings });
|
||||
for (const section of sections) {
|
||||
if (section.name === name) return section;
|
||||
|
@ -81,7 +81,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
throw new Error(`Invalid section name: ${name}`);
|
||||
}
|
||||
|
||||
screenFromName(screenName:string) {
|
||||
screenFromName(screenName: string) {
|
||||
if (screenName === 'encryption') return <EncryptionConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
|
||||
|
@ -89,7 +89,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
throw new Error(`Invalid screen name: ${screenName}`);
|
||||
}
|
||||
|
||||
switchSection(name:string) {
|
||||
switchSection(name: string) {
|
||||
const section = this.sectionByName(name);
|
||||
let screenName = '';
|
||||
if (section.isScreen) {
|
||||
|
@ -104,11 +104,11 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
this.setState({ selectedSectionName: section.name, screenName: screenName });
|
||||
}
|
||||
|
||||
sideBar_selectionChange(event:any) {
|
||||
sideBar_selectionChange(event: any) {
|
||||
this.switchSection(event.section.name);
|
||||
}
|
||||
|
||||
keyValueToArray(kv:any) {
|
||||
keyValueToArray(kv: any) {
|
||||
const output = [];
|
||||
for (const k in kv) {
|
||||
if (!kv.hasOwnProperty(k)) continue;
|
||||
|
@ -121,7 +121,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
return output;
|
||||
}
|
||||
|
||||
renderSectionDescription(section:any) {
|
||||
renderSectionDescription(section: any) {
|
||||
const description = Setting.sectionDescription(section.name);
|
||||
if (!description) return null;
|
||||
|
||||
|
@ -133,10 +133,10 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
);
|
||||
}
|
||||
|
||||
sectionToComponent(key:string, section:any, settings:any, selected:boolean) {
|
||||
sectionToComponent(key: string, section: any, settings: any, selected: boolean) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const createSettingComponents = (advanced:boolean) => {
|
||||
const createSettingComponents = (advanced: boolean) => {
|
||||
const output = [];
|
||||
for (let i = 0; i < section.metadatas.length; i++) {
|
||||
const md = section.metadatas[i];
|
||||
|
@ -150,7 +150,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
const settingComps = createSettingComponents(false);
|
||||
const advancedSettingComps = createSettingComponents(true);
|
||||
|
||||
const sectionStyle:any = {
|
||||
const sectionStyle: any = {
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
maxWidth: 640,
|
||||
|
@ -261,10 +261,10 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
);
|
||||
}
|
||||
|
||||
settingToComponent(key:string, value:any) {
|
||||
settingToComponent(key: string, value: any) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const output:any = null;
|
||||
const output: any = null;
|
||||
|
||||
const rowStyle = {
|
||||
marginBottom: theme.mainPadding,
|
||||
|
@ -317,7 +317,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
paddingBottom: 4,
|
||||
});
|
||||
|
||||
const updateSettingValue = (key:string, value:any) => {
|
||||
const updateSettingValue = (key: string, value: any) => {
|
||||
// console.info(key + ' = ' + value);
|
||||
return shared.updateSettingValue(this, key, value);
|
||||
};
|
||||
|
@ -359,7 +359,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
<select
|
||||
value={value}
|
||||
style={selectStyle}
|
||||
onChange={(event:any) => {
|
||||
onChange={(event: any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
}}
|
||||
>
|
||||
|
@ -404,7 +404,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_STRING) {
|
||||
const inputStyle:any = Object.assign({}, textInputBaseStyle, {
|
||||
const inputStyle: any = Object.assign({}, textInputBaseStyle, {
|
||||
width: '50%',
|
||||
minWidth: '20em',
|
||||
});
|
||||
|
@ -413,13 +413,13 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
if (md.subType === 'file_path_and_args') {
|
||||
inputStyle.marginBottom = subLabel.marginBottom;
|
||||
|
||||
const splitCmd = (cmdString:string) => {
|
||||
const splitCmd = (cmdString: string) => {
|
||||
const path = pathUtils.extractExecutablePath(cmdString);
|
||||
const args = cmdString.substr(path.length + 1);
|
||||
return [pathUtils.unquotePath(path), args];
|
||||
};
|
||||
|
||||
const joinCmd = (cmdArray:string[]) => {
|
||||
const joinCmd = (cmdArray: string[]) => {
|
||||
if (!cmdArray[0] && !cmdArray[1]) return '';
|
||||
let cmdString = pathUtils.quotePath(cmdArray[0]);
|
||||
if (!cmdString) cmdString = '""';
|
||||
|
@ -427,13 +427,13 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
return cmdString;
|
||||
};
|
||||
|
||||
const onPathChange = (event:any) => {
|
||||
const onPathChange = (event: any) => {
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[0] = event.target.value;
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
};
|
||||
|
||||
const onArgsChange = (event:any) => {
|
||||
const onArgsChange = (event: any) => {
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[1] = event.target.value;
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
|
@ -462,7 +462,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
<input
|
||||
type={inputType}
|
||||
style={Object.assign({}, inputStyle, { marginBottom: 0, marginRight: 5 })}
|
||||
onChange={(event:any) => {
|
||||
onChange={(event: any) => {
|
||||
onPathChange(event);
|
||||
}}
|
||||
value={cmd[0]}
|
||||
|
@ -479,7 +479,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
<input
|
||||
type={inputType}
|
||||
style={inputStyle}
|
||||
onChange={(event:any) => {
|
||||
onChange={(event: any) => {
|
||||
onArgsChange(event);
|
||||
}}
|
||||
value={cmd[1]}
|
||||
|
@ -495,7 +495,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
</div>
|
||||
);
|
||||
} else {
|
||||
const onTextChange = (event:any) => {
|
||||
const onTextChange = (event: any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
};
|
||||
|
||||
|
@ -508,7 +508,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
type={inputType}
|
||||
style={inputStyle}
|
||||
value={this.state.settings[key]}
|
||||
onChange={(event:any) => {
|
||||
onChange={(event: any) => {
|
||||
onTextChange(event);
|
||||
}}
|
||||
/>
|
||||
|
@ -519,14 +519,14 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
);
|
||||
}
|
||||
} else if (md.type === Setting.TYPE_INT) {
|
||||
const onNumChange = (event:any) => {
|
||||
const onNumChange = (event: any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
};
|
||||
|
||||
const label = [md.label()];
|
||||
if (md.unitLabel) label.push(`(${md.unitLabel()})`);
|
||||
|
||||
const inputStyle:any = Object.assign({}, textInputBaseStyle);
|
||||
const inputStyle: any = Object.assign({}, textInputBaseStyle);
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
|
@ -537,7 +537,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
type="number"
|
||||
style={inputStyle}
|
||||
value={this.state.settings[key]}
|
||||
onChange={(event:any) => {
|
||||
onChange={(event: any) => {
|
||||
onNumChange(event);
|
||||
}}
|
||||
min={md.minimum}
|
||||
|
@ -640,7 +640,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
const mapStateToProps = (state: any) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
settings: state.settings,
|
||||
|
|
|
@ -3,14 +3,14 @@ const styled = require('styled-components').default;
|
|||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
|
||||
interface Props {
|
||||
selection: string,
|
||||
onSelectionChange: Function,
|
||||
sections: any[],
|
||||
selection: string;
|
||||
onSelectionChange: Function;
|
||||
sections: any[];
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
display: flex;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor2};
|
||||
background-color: ${(props: any) => props.theme.backgroundColor2};
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
|
@ -18,22 +18,22 @@ export const StyledListItem = styled.a`
|
|||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||
background: ${(props:any) => props.selected ? props.theme.selectedColor2 : 'none'};
|
||||
padding: ${(props: any) => props.theme.mainPadding}px;
|
||||
background: ${(props: any) => props.selected ? props.theme.selectedColor2 : 'none'};
|
||||
transition: 0.1s;
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
opacity: ${(props:any) => props.selected ? 1 : 0.8};
|
||||
opacity: ${(props: any) => props.selected ? 1 : 0.8};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover2};
|
||||
background-color: ${(props: any) => props.theme.backgroundColorHover2};
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledListItemLabel = styled.span`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.2)}px;
|
||||
font-size: ${(props: any) => Math.round(props.theme.fontSize * 1.2)}px;
|
||||
font-weight: 500;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
color: ${(props: any) => props.theme.color2};
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
@ -42,15 +42,15 @@ export const StyledListItemLabel = styled.span`
|
|||
`;
|
||||
|
||||
export const StyledListItemIcon = styled.i`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.4)}px;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
margin-right: ${(props:any) => props.theme.mainPadding / 1.5}px;
|
||||
font-size: ${(props: any) => Math.round(props.theme.fontSize * 1.4)}px;
|
||||
color: ${(props: any) => props.theme.color2};
|
||||
margin-right: ${(props: any) => props.theme.mainPadding / 1.5}px;
|
||||
`;
|
||||
|
||||
export default function SideBar(props:Props) {
|
||||
const buttons:any[] = [];
|
||||
export default function SideBar(props: Props) {
|
||||
const buttons: any[] = [];
|
||||
|
||||
function renderButton(section:any) {
|
||||
function renderButton(section: any) {
|
||||
const selected = props.selection === section.name;
|
||||
return (
|
||||
<StyledListItem key={section.name} selected={selected} onClick={() => { props.onSelectionChange({ section: section }); }}>
|
||||
|
|
|
@ -8,17 +8,17 @@ const { themeStyle } = require('@joplin/lib/theme');
|
|||
const Shared = require('@joplin/lib/components/shared/dropbox-login-shared');
|
||||
|
||||
interface Props {
|
||||
themeId: string,
|
||||
themeId: string;
|
||||
}
|
||||
|
||||
class DropboxLoginScreenComponent extends React.Component<any, any> {
|
||||
|
||||
shared_:any;
|
||||
shared_: any;
|
||||
|
||||
constructor(props:Props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.shared_ = new Shared(this, (msg:string) => bridge().showInfoMessageBox(msg), (msg:string) => bridge().showErrorMessageBox(msg));
|
||||
this.shared_ = new Shared(this, (msg: string) => bridge().showInfoMessageBox(msg), (msg: string) => bridge().showErrorMessageBox(msg));
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
|
@ -61,7 +61,7 @@ class DropboxLoginScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
const mapStateToProps = (state: any) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
|
|
|
@ -5,9 +5,9 @@ const ipcRenderer = require('electron').ipcRenderer;
|
|||
|
||||
export default class ErrorBoundary extends React.Component {
|
||||
|
||||
state:any = { error: null, errorInfo: null };
|
||||
state: any = { error: null, errorInfo: null };
|
||||
|
||||
componentDidCatch(error:any, errorInfo:any) {
|
||||
componentDidCatch(error: any, errorInfo: any) {
|
||||
this.setState({ error: error, errorInfo: errorInfo });
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ const shim = require('@joplin/lib/shim').default;
|
|||
const keymapService = KeymapService.instance();
|
||||
|
||||
export interface KeymapConfigScreenProps {
|
||||
themeId: number
|
||||
themeId: number;
|
||||
}
|
||||
|
||||
export const KeymapConfigScreen = ({ themeId }: KeymapConfigScreenProps) => {
|
||||
|
@ -27,7 +27,7 @@ export const KeymapConfigScreen = ({ themeId }: KeymapConfigScreenProps) => {
|
|||
const [editing, enableEditing, disableEditing] = useCommandStatus();
|
||||
const [hovering, enableHovering, disableHovering] = useCommandStatus();
|
||||
|
||||
const handleSave = (event: { commandName: string, accelerator: string }) => {
|
||||
const handleSave = (event: { commandName: string; accelerator: string }) => {
|
||||
const { commandName, accelerator } = event;
|
||||
setAccelerator(commandName, accelerator);
|
||||
disableEditing(commandName);
|
||||
|
|
|
@ -8,13 +8,13 @@ import { _ } from '@joplin/lib/locale';
|
|||
const keymapService = KeymapService.instance();
|
||||
|
||||
export interface ShortcutRecorderProps {
|
||||
onSave: (event: { commandName: string, accelerator: string }) => void,
|
||||
onReset: (event: { commandName: string }) => void,
|
||||
onCancel: (event: { commandName: string }) => void,
|
||||
onError: (event: { recorderError: Error }) => void,
|
||||
initialAccelerator: string
|
||||
commandName: string,
|
||||
themeId: number
|
||||
onSave: (event: { commandName: string; accelerator: string })=> void;
|
||||
onReset: (event: { commandName: string })=> void;
|
||||
onCancel: (event: { commandName: string })=> void;
|
||||
onError: (event: { recorderError: Error })=> void;
|
||||
initialAccelerator: string;
|
||||
commandName: string;
|
||||
themeId: number;
|
||||
}
|
||||
|
||||
export const ShortcutRecorder = ({ onSave, onReset, onCancel, onError, initialAccelerator, commandName, themeId }: ShortcutRecorderProps) => {
|
||||
|
|
|
@ -25,6 +25,7 @@ export default function styles(themeId: number) {
|
|||
recorderInput: {
|
||||
...theme.inputStyle,
|
||||
minHeight: 29,
|
||||
width: '200px',
|
||||
},
|
||||
label: {
|
||||
...theme.textStyle,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { _ } from '@joplin/lib/locale';
|
|||
|
||||
const commandService = CommandService.instance();
|
||||
|
||||
const getLabel = (commandName: string):string => {
|
||||
const getLabel = (commandName: string): string => {
|
||||
if (commandService.exists(commandName)) return commandService.label(commandName, true);
|
||||
|
||||
// Some commands are not registered in CommandService at the moment
|
||||
|
|
|
@ -4,10 +4,10 @@ import KeymapService from '@joplin/lib/services/KeymapService';
|
|||
const keymapService = KeymapService.instance();
|
||||
|
||||
interface CommandStatus {
|
||||
[commandName: string]: boolean
|
||||
[commandName: string]: boolean;
|
||||
}
|
||||
|
||||
const useCommandStatus = (): [CommandStatus, (commandName: string) => void, (commandName: string) => void] => {
|
||||
const useCommandStatus = (): [CommandStatus, (commandName: string)=> void, (commandName: string)=> void] => {
|
||||
const [status, setStatus] = useState<CommandStatus>(() =>
|
||||
keymapService.getCommandNames().reduce((accumulator: CommandStatus, command: string) => {
|
||||
accumulator[command] = false;
|
||||
|
|
|
@ -10,7 +10,7 @@ const keymapService = KeymapService.instance();
|
|||
function allKeymapItems() {
|
||||
const output = keymapService.getKeymapItems().slice();
|
||||
|
||||
output.sort((a:KeymapItem, b:KeymapItem) => {
|
||||
output.sort((a: KeymapItem, b: KeymapItem) => {
|
||||
return getLabel(a.command).toLocaleLowerCase() < getLabel(b.command).toLocaleLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
|
@ -20,9 +20,9 @@ function allKeymapItems() {
|
|||
const useKeymap = (): [
|
||||
KeymapItem[],
|
||||
Error,
|
||||
(keymapItems: KeymapItem[]) => void,
|
||||
(commandName: string, accelerator: string) => void,
|
||||
(commandName: string) => void
|
||||
(keymapItems: KeymapItem[])=> void,
|
||||
(commandName: string, accelerator: string)=> void,
|
||||
(commandName: string)=> void,
|
||||
] => {
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => allKeymapItems());
|
||||
const [keymapError, setKeymapError] = useState<Error>(null);
|
||||
|
|
|
@ -72,13 +72,13 @@ const commands = [
|
|||
|
||||
class MainScreenComponent extends React.Component<any, any> {
|
||||
|
||||
waitForNotesSavedIID_:any;
|
||||
isPrinting_:boolean;
|
||||
styleKey_:string;
|
||||
styles_:any;
|
||||
promptOnClose_:Function;
|
||||
waitForNotesSavedIID_: any;
|
||||
isPrinting_: boolean;
|
||||
styleKey_: string;
|
||||
styles_: any;
|
||||
promptOnClose_: Function;
|
||||
|
||||
constructor(props:any) {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -109,7 +109,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
window.addEventListener('resize', this.window_resize);
|
||||
}
|
||||
|
||||
buildLayout(plugins:any):LayoutItem {
|
||||
buildLayout(plugins: any): LayoutItem {
|
||||
const rootLayoutSize = this.rootLayoutSize();
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const sideBarMinWidth = 200;
|
||||
|
@ -131,7 +131,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
if (sizes[k].width < sideBarMinWidth) sizes[k].width = sideBarMinWidth;
|
||||
}
|
||||
|
||||
const pluginColumnChildren:LayoutItem[] = [];
|
||||
const pluginColumnChildren: LayoutItem[] = [];
|
||||
|
||||
const infos = pluginUtils.viewInfosByType(plugins, 'webview');
|
||||
|
||||
|
@ -264,16 +264,16 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
|
||||
updateRootLayoutSize() {
|
||||
this.setState({ layout: produce(this.state.layout, (draft:any) => {
|
||||
this.setState({ layout: produce(this.state.layout, (draft: any) => {
|
||||
const s = this.rootLayoutSize();
|
||||
draft.width = s.width;
|
||||
draft.height = s.height;
|
||||
}) });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps:any, prevState:any) {
|
||||
componentDidUpdate(prevProps: any, prevState: any) {
|
||||
if (this.props.noteListVisibility !== prevProps.noteListVisibility || this.props.sidebarVisibility !== prevProps.sidebarVisibility) {
|
||||
this.setState({ layout: produce(this.state.layout, (draft:any) => {
|
||||
this.setState({ layout: produce(this.state.layout, (draft: any) => {
|
||||
const noteListColumn = findItemByKey(draft, 'noteListColumn');
|
||||
noteListColumn.visible = this.props.noteListVisibility;
|
||||
|
||||
|
@ -337,14 +337,14 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
});
|
||||
}
|
||||
|
||||
async waitForNoteToSaved(noteId:string) {
|
||||
async waitForNoteToSaved(noteId: string) {
|
||||
while (noteId && this.props.editorNoteStatuses[noteId] === 'saving') {
|
||||
console.info('Waiting for note to be saved...', this.props.editorNoteStatuses);
|
||||
await time.msleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
async printTo_(target:string, options:any) {
|
||||
async printTo_(target: string, options: any) {
|
||||
// Concurrent print calls are disallowed to avoid incorrect settings being restored upon completion
|
||||
if (this.isPrinting_) {
|
||||
console.info(`Printing ${options.path} to ${target} disallowed, already printing.`);
|
||||
|
@ -399,7 +399,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
return 50;
|
||||
}
|
||||
|
||||
styles(themeId:number, width:number, height:number, messageBoxVisible:boolean, isSidebarVisible:any, isNoteListVisible:any) {
|
||||
styles(themeId: number, width: number, height: number, messageBoxVisible: boolean, isSidebarVisible: any, isNoteListVisible: any) {
|
||||
const styleKey = [themeId, width, height, messageBoxVisible, +isSidebarVisible, +isNoteListVisible].join('_');
|
||||
if (styleKey === this.styleKey_) return this.styles_;
|
||||
|
||||
|
@ -449,7 +449,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
return this.styles_;
|
||||
}
|
||||
|
||||
renderNotification(theme:any, styles:any) {
|
||||
renderNotification(theme: any, styles: any) {
|
||||
if (!this.messageBoxVisible()) return null;
|
||||
|
||||
const onViewStatusScreen = () => {
|
||||
|
@ -539,7 +539,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
);
|
||||
}
|
||||
|
||||
messageBoxVisible(props:any = null) {
|
||||
messageBoxVisible(props: any = null) {
|
||||
if (!props) props = this.props;
|
||||
return props.hasDisabledSyncItems || props.showMissingMasterKeyMessage || props.showNeedUpgradingMasterKeyMessage || props.showShouldReencryptMessage || props.hasDisabledEncryptionItems || this.props.shouldUpgradeSyncTarget;
|
||||
}
|
||||
|
@ -556,16 +556,16 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
}
|
||||
|
||||
userWebview_message(event:any) {
|
||||
userWebview_message(event: any) {
|
||||
PluginService.instance().pluginById(event.pluginId).viewController(event.viewId).emitMessage(event);
|
||||
}
|
||||
|
||||
resizableLayout_resize(event:any) {
|
||||
resizableLayout_resize(event: any) {
|
||||
this.setState({ layout: event.layout });
|
||||
Setting.setValue('ui.layout', allDynamicSizes(event.layout));
|
||||
}
|
||||
|
||||
resizableLayout_renderItem(key:string, event:any) {
|
||||
resizableLayout_renderItem(key: string, event: any) {
|
||||
const eventEmitter = event.eventEmitter;
|
||||
|
||||
if (key === 'sideBar') {
|
||||
|
@ -640,7 +640,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
const styles = this.styles(this.props.themeId, style.width, style.height, this.messageBoxVisible(), sidebarVisibility, noteListVisibility);
|
||||
|
||||
if (!this.promptOnClose_) {
|
||||
this.promptOnClose_ = (answer:any, buttonType:any) => {
|
||||
this.promptOnClose_ = (answer: any, buttonType: any) => {
|
||||
return this.state.promptOptions.onClose(answer, buttonType);
|
||||
};
|
||||
}
|
||||
|
@ -680,7 +680,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
const mapStateToProps = (state: any) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
settingEditorCodeView: state.settings['editor.codeView'],
|
||||
|
|
|
@ -5,15 +5,15 @@ import { stateUtils } from '@joplin/lib/reducer';
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
const time = require('@joplin/lib/time').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'editAlarm',
|
||||
label: () => _('Set alarm'),
|
||||
iconName: 'icon-alarm',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
const note = await Note.load(noteId);
|
||||
|
@ -28,7 +28,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
inputType: 'datetime',
|
||||
buttons: ['ok', 'cancel', 'clear'],
|
||||
value: note.todo_due ? new Date(note.todo_due) : defaultDate,
|
||||
onClose: async (answer:any, buttonType:string) => {
|
||||
onClose: async (answer: any, buttonType: string) => {
|
||||
let newNote = null;
|
||||
|
||||
if (buttonType === 'clear') {
|
||||
|
@ -56,7 +56,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
|
||||
enabledCondition: 'oneNoteSelected && noteIsTodo && !noteTodoCompleted',
|
||||
|
||||
mapStateToTitle: (state:any) => {
|
||||
mapStateToTitle: (state: any) => {
|
||||
const note = stateUtils.selectedNote(state);
|
||||
return note && note.todo_due ? time.formatMsToLocal(note.todo_due) : null;
|
||||
},
|
||||
|
|
|
@ -5,14 +5,14 @@ import { _ } from '@joplin/lib/locale';
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'exportPdf',
|
||||
label: () => `PDF - ${_('PDF File')}`,
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
execute: async (context: CommandContext, noteIds: string[] = null) => {
|
||||
try {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'hideModalMessage',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.setState({ modalLayer: { visible: false, message: '' } });
|
||||
|
|
|
@ -3,21 +3,21 @@ import { _ } from '@joplin/lib/locale';
|
|||
const Folder = require('@joplin/lib/models/Folder');
|
||||
const Note = require('@joplin/lib/models/Note');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'moveToFolder',
|
||||
label: () => _('Move to notebook'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
execute: async (context: CommandContext, noteIds: string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
const folders:any[] = await Folder.sortFolderTree();
|
||||
const startFolders:any[] = [];
|
||||
const folders: any[] = await Folder.sortFolderTree();
|
||||
const startFolders: any[] = [];
|
||||
const maxDepth = 15;
|
||||
|
||||
const addOptions = (folders:any[], depth:number) => {
|
||||
const addOptions = (folders: any[], depth: number) => {
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
const folder = folders[i];
|
||||
startFolders.push({ key: folder.id, value: folder.id, label: folder.title, indentDepth: depth });
|
||||
|
@ -33,7 +33,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
inputType: 'dropdown',
|
||||
value: '',
|
||||
autocomplete: startFolders,
|
||||
onClose: async (answer:any) => {
|
||||
onClose: async (answer: any) => {
|
||||
if (answer != null) {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Note.moveToFolder(noteIds[i], answer.value);
|
||||
|
|
|
@ -3,23 +3,23 @@ import { _ } from '@joplin/lib/locale';
|
|||
const Folder = require('@joplin/lib/models/Folder');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'newFolder',
|
||||
label: () => _('New notebook'),
|
||||
iconName: 'fa-book',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:CommandContext, parentId:string = null) => {
|
||||
execute: async (_context: CommandContext, parentId: string = null) => {
|
||||
comp.setState({
|
||||
promptOptions: {
|
||||
label: _('Notebook title:'),
|
||||
onClose: async (answer:string) => {
|
||||
onClose: async (answer: string) => {
|
||||
if (answer) {
|
||||
let folder = null;
|
||||
try {
|
||||
const toSave:any = { title: answer };
|
||||
const toSave: any = { title: answer };
|
||||
if (parentId) toSave.parent_id = parentId;
|
||||
folder = await Folder.save(toSave, { userSideValidation: true });
|
||||
} catch (error) {
|
||||
|
|
|
@ -4,15 +4,15 @@ const Setting = require('@joplin/lib/models/Setting').default;
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
const TemplateUtils = require('@joplin/lib/TemplateUtils');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'newNote',
|
||||
label: () => _('New note'),
|
||||
iconName: 'fa-file',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:CommandContext, template:string = null, isTodo:boolean = false) => {
|
||||
execute: async (_context: CommandContext, template: string = null, isTodo: boolean = false) => {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
if (!folderId) return;
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'newSubFolder',
|
||||
label: () => _('New sub-notebook'),
|
||||
iconName: 'fa-book',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, parentId:string = null) => {
|
||||
execute: async (context: CommandContext, parentId: string = null) => {
|
||||
parentId = parentId || context.state.selectedFolderId;
|
||||
return CommandService.instance().execute('newFolder', parentId);
|
||||
},
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'newTodo',
|
||||
label: () => _('New to-do'),
|
||||
iconName: 'fa-check-square',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:CommandContext, template:string = null) => {
|
||||
execute: async (_context: CommandContext, template: string = null) => {
|
||||
return CommandService.instance().execute('newNote', template, true);
|
||||
},
|
||||
enabledCondition: 'oneFolderSelected && !inConflictFolder',
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openFolder',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, folderId:string) => {
|
||||
execute: async (context: CommandContext, folderId: string) => {
|
||||
context.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folderId,
|
||||
|
|
|
@ -2,13 +2,13 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
const Folder = require('@joplin/lib/models/Folder');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openNote',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string, hash:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string, hash: string = null) => {
|
||||
const note = await Note.load(noteId);
|
||||
if (!note) throw new Error(`No such note: ${noteId}`);
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openTag',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, tagId:string) => {
|
||||
execute: async (context: CommandContext, tagId: string) => {
|
||||
context.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
id: tagId,
|
||||
|
|
|
@ -2,15 +2,15 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
|
|||
import { _ } from '@joplin/lib/locale';
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'print',
|
||||
label: () => _('Print'),
|
||||
iconName: 'fa-file',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
execute: async (context: CommandContext, noteIds: string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
try {
|
||||
|
|
|
@ -3,14 +3,14 @@ import { _ } from '@joplin/lib/locale';
|
|||
const Folder = require('@joplin/lib/models/Folder');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'renameFolder',
|
||||
label: () => _('Rename'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, folderId:string = null) => {
|
||||
execute: async (context: CommandContext, folderId: string = null) => {
|
||||
folderId = folderId || context.state.selectedFolderId;
|
||||
|
||||
const folder = await Folder.load(folderId);
|
||||
|
@ -20,7 +20,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
promptOptions: {
|
||||
label: _('Rename notebook:'),
|
||||
value: folder.title,
|
||||
onClose: async (answer:string) => {
|
||||
onClose: async (answer: string) => {
|
||||
if (answer !== null) {
|
||||
try {
|
||||
folder.title = answer;
|
||||
|
|
|
@ -3,14 +3,14 @@ import { _ } from '@joplin/lib/locale';
|
|||
const Tag = require('@joplin/lib/models/Tag');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'renameTag',
|
||||
label: () => _('Rename'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, tagId:string = null) => {
|
||||
execute: async (context: CommandContext, tagId: string = null) => {
|
||||
tagId = tagId || context.state.selectedTagId;
|
||||
if (!tagId) return;
|
||||
|
||||
|
@ -20,7 +20,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
promptOptions: {
|
||||
label: _('Rename tag:'),
|
||||
value: tag.title,
|
||||
onClose: async (answer:string) => {
|
||||
onClose: async (answer: string) => {
|
||||
if (answer !== null) {
|
||||
try {
|
||||
tag.title = answer;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'search',
|
||||
iconName: 'icon-search',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
// Doesn't do anything for now but could be used to
|
||||
|
|
|
@ -2,20 +2,20 @@ import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } fr
|
|||
import { _ } from '@joplin/lib/locale';
|
||||
const TemplateUtils = require('@joplin/lib/TemplateUtils');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'selectTemplate',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:CommandContext, noteType:string) => {
|
||||
execute: async (_context: CommandContext, noteType: string) => {
|
||||
comp.setState({
|
||||
promptOptions: {
|
||||
label: _('Template file:'),
|
||||
inputType: 'dropdown',
|
||||
value: comp.props.templates[0], // Need to start with some value
|
||||
autocomplete: comp.props.templates,
|
||||
onClose: async (answer:any) => {
|
||||
onClose: async (answer: any) => {
|
||||
if (answer) {
|
||||
if (noteType === 'note' || noteType === 'todo') {
|
||||
CommandService.instance().execute('newNote', answer.value, noteType === 'todo');
|
||||
|
|
|
@ -2,32 +2,32 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
|
|||
import { _ } from '@joplin/lib/locale';
|
||||
const Tag = require('@joplin/lib/models/Tag');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'setTags',
|
||||
label: () => _('Tags'),
|
||||
iconName: 'icon-tags',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
execute: async (context: CommandContext, noteIds: string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
const tags = await Tag.commonTagsByNoteIds(noteIds);
|
||||
const startTags = tags
|
||||
.map((a:any) => {
|
||||
.map((a: any) => {
|
||||
return { value: a.id, label: a.title };
|
||||
})
|
||||
.sort((a:any, b:any) => {
|
||||
.sort((a: any, b: any) => {
|
||||
// sensitivity accent will treat accented characters as differemt
|
||||
// but treats caps as equal
|
||||
return a.label.localeCompare(b.label, undefined, { sensitivity: 'accent' });
|
||||
});
|
||||
const allTags = await Tag.allWithNotes();
|
||||
const tagSuggestions = allTags.map((a:any) => {
|
||||
const tagSuggestions = allTags.map((a: any) => {
|
||||
return { value: a.id, label: a.title };
|
||||
})
|
||||
.sort((a:any, b:any) => {
|
||||
.sort((a: any, b: any) => {
|
||||
// sensitivity accent will treat accented characters as differemt
|
||||
// but treats caps as equal
|
||||
return a.label.localeCompare(b.label, undefined, { sensitivity: 'accent' });
|
||||
|
@ -39,7 +39,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
inputType: 'tags',
|
||||
value: startTags,
|
||||
autocomplete: tagSuggestions,
|
||||
onClose: async (answer:any[]) => {
|
||||
onClose: async (answer: any[]) => {
|
||||
if (answer !== null) {
|
||||
const endTagTitles = answer.map(a => {
|
||||
return a.label.trim();
|
||||
|
@ -47,16 +47,16 @@ export const runtime = (comp:any):CommandRuntime => {
|
|||
if (noteIds.length === 1) {
|
||||
await Tag.setNoteTagsByTitles(noteIds[0], endTagTitles);
|
||||
} else {
|
||||
const startTagTitles = startTags.map((a:any) => { return a.label.trim(); });
|
||||
const addTags = endTagTitles.filter((value:string) => !startTagTitles.includes(value));
|
||||
const delTags = startTagTitles.filter((value:string) => !endTagTitles.includes(value));
|
||||
const startTagTitles = startTags.map((a: any) => { return a.label.trim(); });
|
||||
const addTags = endTagTitles.filter((value: string) => !startTagTitles.includes(value));
|
||||
const delTags = startTagTitles.filter((value: string) => !endTagTitles.includes(value));
|
||||
|
||||
// apply the tag additions and deletions to each selected note
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const tags = await Tag.tagsByNoteId(noteIds[i]);
|
||||
let tagTitles = tags.map((a:any) => { return a.title; });
|
||||
let tagTitles = tags.map((a: any) => { return a.title; });
|
||||
tagTitles = tagTitles.concat(addTags);
|
||||
tagTitles = tagTitles.filter((value:string) => !delTags.includes(value));
|
||||
tagTitles = tagTitles.filter((value: string) => !delTags.includes(value));
|
||||
await Tag.setNoteTagsByTitles(noteIds[i], tagTitles);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import * as React from 'react';
|
||||
import { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showModalMessage',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context:CommandContext, message:string) => {
|
||||
execute: async (_context: CommandContext, message: string) => {
|
||||
let brIndex = 1;
|
||||
const lines = message.split('\n').map((line:string) => {
|
||||
const lines = message.split('\n').map((line: string) => {
|
||||
if (!line.trim()) return <br key={`${brIndex++}`}/>;
|
||||
return <div key={line} className="text">{line}</div>;
|
||||
});
|
||||
|
|
|
@ -3,14 +3,14 @@ import { _ } from '@joplin/lib/locale';
|
|||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
const Note = require('@joplin/lib/models/Note');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showNoteContentProperties',
|
||||
label: () => _('Statistics...'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
const note = await Note.load(noteId);
|
||||
|
|
|
@ -2,15 +2,15 @@ import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } fr
|
|||
import { _ } from '@joplin/lib/locale';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showNoteProperties',
|
||||
label: () => _('Note properties'),
|
||||
iconName: 'icon-info',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
comp.setState({
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showShareNoteDialog',
|
||||
label: () => _('Share note...'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
execute: async (context: CommandContext, noteIds: string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
comp.setState({
|
||||
|
|
|
@ -6,15 +6,15 @@ import { AppState } from '../../../app';
|
|||
|
||||
const Menu = bridge().Menu;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showSpellCheckerMenu',
|
||||
label: () => _('Spell checker'),
|
||||
iconName: 'fas fa-globe',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, selectedLanguage:string = null, useSpellChecker:boolean = null) => {
|
||||
execute: async (context: CommandContext, selectedLanguage: string = null, useSpellChecker: boolean = null) => {
|
||||
selectedLanguage = selectedLanguage === null ? context.state.settings['spellChecker.language'] : selectedLanguage;
|
||||
useSpellChecker = useSpellChecker === null ? context.state.settings['spellChecker.enabled'] : useSpellChecker;
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const runtime = ():CommandRuntime => {
|
|||
menu.popup(bridge().window());
|
||||
},
|
||||
|
||||
mapStateToTitle(state:AppState):string {
|
||||
mapStateToTitle(state: AppState): string {
|
||||
if (!state.settings['spellChecker.enabled']) return null;
|
||||
const language = state.settings['spellChecker.language'];
|
||||
if (!language) return null;
|
||||
|
|
|
@ -3,15 +3,15 @@ import Setting from '@joplin/lib/models/Setting';
|
|||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleEditors',
|
||||
label: () => _('Toggle editors'),
|
||||
iconName: 'fa-columns',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext) => {
|
||||
execute: async (context: CommandContext) => {
|
||||
// A bit of a hack, but for now don't allow changing code view
|
||||
// while a note is being saved as it will cause a problem with
|
||||
// TinyMCE because it won't have time to send its content before
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleNoteList',
|
||||
label: () => _('Toggle note list'),
|
||||
iconName: 'fas fa-align-justify',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.props.dispatch({
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleSideBar',
|
||||
label: () => _('Toggle sidebar'),
|
||||
iconName: 'fas fa-bars',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.props.dispatch({
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleVisiblePanes',
|
||||
label: () => _('Toggle editor layout'),
|
||||
iconName: 'icon-layout ',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.props.dispatch({
|
||||
|
|
|
@ -28,8 +28,8 @@ const TemplateUtils = require('@joplin/lib/TemplateUtils');
|
|||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
function pluginMenuItemsCommandNames(menuItems:MenuItem[]):string[] {
|
||||
let output:string[] = [];
|
||||
function pluginMenuItemsCommandNames(menuItems: MenuItem[]): string[] {
|
||||
let output: string[] = [];
|
||||
for (const menuItem of menuItems) {
|
||||
if (menuItem.submenu) {
|
||||
output = output.concat(pluginMenuItemsCommandNames(menuItem.submenu));
|
||||
|
@ -40,8 +40,8 @@ function pluginMenuItemsCommandNames(menuItems:MenuItem[]):string[] {
|
|||
return output;
|
||||
}
|
||||
|
||||
function pluginCommandNames(plugins:PluginStates):string[] {
|
||||
let output:string[] = [];
|
||||
function pluginCommandNames(plugins: PluginStates): string[] {
|
||||
let output: string[] = [];
|
||||
|
||||
for (const view of pluginUtils.viewsByType(plugins, 'menu')) {
|
||||
output = output.concat(pluginMenuItemsCommandNames(view.menuItems));
|
||||
|
@ -54,8 +54,8 @@ function pluginCommandNames(plugins:PluginStates):string[] {
|
|||
return output;
|
||||
}
|
||||
|
||||
function createPluginMenuTree(label:string, menuItems:MenuItem[], onMenuItemClick:Function) {
|
||||
const output:any = {
|
||||
function createPluginMenuTree(label: string, menuItems: MenuItem[], onMenuItemClick: Function) {
|
||||
const output: any = {
|
||||
label: label,
|
||||
submenu: [],
|
||||
};
|
||||
|
@ -72,50 +72,50 @@ function createPluginMenuTree(label:string, menuItems:MenuItem[], onMenuItemClic
|
|||
}
|
||||
|
||||
interface Props {
|
||||
dispatch: Function,
|
||||
menuItemProps: any,
|
||||
routeName: string,
|
||||
selectedFolderId: string,
|
||||
layoutButtonSequence: number,
|
||||
['notes.sortOrder.field']: string,
|
||||
['folders.sortOrder.field']: string,
|
||||
['notes.sortOrder.reverse']: boolean,
|
||||
['folders.sortOrder.reverse']: boolean,
|
||||
showNoteCounts: boolean,
|
||||
uncompletedTodosOnTop: boolean,
|
||||
showCompletedTodos: boolean,
|
||||
pluginMenuItems: any[],
|
||||
pluginMenus: any[],
|
||||
['spellChecker.enabled']: boolean,
|
||||
['spellChecker.language']: string,
|
||||
dispatch: Function;
|
||||
menuItemProps: any;
|
||||
routeName: string;
|
||||
selectedFolderId: string;
|
||||
layoutButtonSequence: number;
|
||||
['notes.sortOrder.field']: string;
|
||||
['folders.sortOrder.field']: string;
|
||||
['notes.sortOrder.reverse']: boolean;
|
||||
['folders.sortOrder.reverse']: boolean;
|
||||
showNoteCounts: boolean;
|
||||
uncompletedTodosOnTop: boolean;
|
||||
showCompletedTodos: boolean;
|
||||
pluginMenuItems: any[];
|
||||
pluginMenus: any[];
|
||||
['spellChecker.enabled']: boolean;
|
||||
['spellChecker.language']: string;
|
||||
}
|
||||
|
||||
const commandNames:string[] = menuCommandNames();
|
||||
const commandNames: string[] = menuCommandNames();
|
||||
|
||||
function menuItemSetChecked(id:string, checked:boolean) {
|
||||
function menuItemSetChecked(id: string, checked: boolean) {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
const menuItem = menu.getMenuItemById(id);
|
||||
if (!menuItem) return;
|
||||
menuItem.checked = checked;
|
||||
}
|
||||
|
||||
function menuItemSetEnabled(id:string, enabled:boolean) {
|
||||
function menuItemSetEnabled(id: string, enabled: boolean) {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
const menuItem = menu.getMenuItemById(id);
|
||||
if (!menuItem) return;
|
||||
menuItem.enabled = enabled;
|
||||
}
|
||||
|
||||
function useMenu(props:Props) {
|
||||
function useMenu(props: Props) {
|
||||
const [menu, setMenu] = useState(null);
|
||||
const [keymapLastChangeTime, setKeymapLastChangeTime] = useState(Date.now());
|
||||
const [modulesLastChangeTime, setModulesLastChangeTime] = useState(Date.now());
|
||||
|
||||
const onMenuItemClick = useCallback((commandName:string) => {
|
||||
const onMenuItemClick = useCallback((commandName: string) => {
|
||||
CommandService.instance().execute(commandName);
|
||||
}, []);
|
||||
|
||||
const onImportModuleClick = useCallback(async (module:Module, moduleSource:string) => {
|
||||
const onImportModuleClick = useCallback(async (module: Module, moduleSource: string) => {
|
||||
let path = null;
|
||||
|
||||
if (moduleSource === 'file') {
|
||||
|
@ -140,8 +140,8 @@ function useMenu(props:Props) {
|
|||
path,
|
||||
format: module.format,
|
||||
outputFormat: module.outputFormat,
|
||||
onProgress: (status:any) => {
|
||||
const statusStrings:string[] = Object.keys(status).map((key:string) => {
|
||||
onProgress: (status: any) => {
|
||||
const statusStrings: string[] = Object.keys(status).map((key: string) => {
|
||||
return `${key}: ${status[key]}`;
|
||||
});
|
||||
|
||||
|
@ -171,8 +171,8 @@ function useMenu(props:Props) {
|
|||
useEffect(() => {
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
const pluginCommandNames = props.pluginMenuItems.map((view:any) => view.commandName);
|
||||
const menuItemDic = menuUtils.commandsToMenuItems(commandNames.concat(pluginCommandNames), (commandName:string) => onMenuItemClickRef.current(commandName));
|
||||
const pluginCommandNames = props.pluginMenuItems.map((view: any) => view.commandName);
|
||||
const menuItemDic = menuUtils.commandsToMenuItems(commandNames.concat(pluginCommandNames), (commandName: string) => onMenuItemClickRef.current(commandName));
|
||||
|
||||
const quitMenuItem = {
|
||||
label: _('Quit'),
|
||||
|
@ -180,7 +180,7 @@ function useMenu(props:Props) {
|
|||
click: () => { bridge().electronApp().quit(); },
|
||||
};
|
||||
|
||||
const sortNoteFolderItems = (type:string) => {
|
||||
const sortNoteFolderItems = (type: string) => {
|
||||
const sortItems = [];
|
||||
const sortOptions = Setting.enumOptions(`${type}.sortOrder.field`);
|
||||
for (const field in sortOptions) {
|
||||
|
@ -223,7 +223,7 @@ function useMenu(props:Props) {
|
|||
|
||||
const importItems = [];
|
||||
const exportItems = [];
|
||||
const templateItems:any[] = [];
|
||||
const templateItems: any[] = [];
|
||||
const ioService = InteropService.instance();
|
||||
const ioModules = ioService.modules();
|
||||
for (let i = 0; i < ioModules.length; i++) {
|
||||
|
@ -233,7 +233,7 @@ function useMenu(props:Props) {
|
|||
exportItems.push({
|
||||
label: module.fullLabel(),
|
||||
click: async () => {
|
||||
await InteropServiceHelper.export((action:any) => props.dispatch(action), module);
|
||||
await InteropServiceHelper.export((action: any) => props.dispatch(action), module);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -314,10 +314,10 @@ function useMenu(props:Props) {
|
|||
},
|
||||
});
|
||||
|
||||
let toolsItems:any[] = [];
|
||||
let toolsItems: any[] = [];
|
||||
|
||||
// we need this workaround, because on macOS the menu is different
|
||||
const toolsItemsWindowsLinux:any[] = [
|
||||
const toolsItemsWindowsLinux: any[] = [
|
||||
{
|
||||
label: _('Options'),
|
||||
accelerator: keymapService.getAccelerator('config'),
|
||||
|
@ -494,7 +494,7 @@ function useMenu(props:Props) {
|
|||
});
|
||||
}
|
||||
|
||||
const rootMenus:any = {
|
||||
const rootMenus: any = {
|
||||
edit: {
|
||||
id: 'edit',
|
||||
label: _('&Edit'),
|
||||
|
@ -668,7 +668,7 @@ function useMenu(props:Props) {
|
|||
// It seems the "visible" property of separators is ignored by Electron, making
|
||||
// it display separators that we want hidden. So this function iterates through
|
||||
// them and remove them completely.
|
||||
const cleanUpSeparators = (items:any[]) => {
|
||||
const cleanUpSeparators = (items: any[]) => {
|
||||
const output = [];
|
||||
for (const item of items) {
|
||||
if ('visible' in item && item.type === 'separator' && !item.visible) continue;
|
||||
|
@ -694,7 +694,7 @@ function useMenu(props:Props) {
|
|||
}
|
||||
|
||||
for (const view of props.pluginMenuItems) {
|
||||
const location:MenuItemLocation = view.location;
|
||||
const location: MenuItemLocation = view.location;
|
||||
if (location === MenuItemLocation.Context) continue;
|
||||
|
||||
const itemParent = rootMenus[location];
|
||||
|
@ -713,7 +713,7 @@ function useMenu(props:Props) {
|
|||
if (!itemParent) {
|
||||
reg.logger().error('Menu location does not exist: ', location, view);
|
||||
} else {
|
||||
itemParent.submenu.push(createPluginMenuTree(view.label, view.menuItems, (commandName:string) => onMenuItemClickRef.current(commandName)));
|
||||
itemParent.submenu.push(createPluginMenuTree(view.label, view.menuItems, (commandName: string) => onMenuItemClickRef.current(commandName)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,7 +765,7 @@ function useMenu(props:Props) {
|
|||
menuItemSetChecked(`layoutButtonSequence_${value}`, props.layoutButtonSequence === Number(value));
|
||||
}
|
||||
|
||||
function applySortItemCheckState(type:string) {
|
||||
function applySortItemCheckState(type: string) {
|
||||
const sortOptions = Setting.enumOptions(`${type}.sortOrder.field`);
|
||||
for (const field in sortOptions) {
|
||||
if (!sortOptions.hasOwnProperty(field)) continue;
|
||||
|
@ -820,13 +820,13 @@ function useMenu(props:Props) {
|
|||
return menu;
|
||||
}
|
||||
|
||||
function MenuBar(props:Props):any {
|
||||
function MenuBar(props: Props): any {
|
||||
const menu = useMenu(props);
|
||||
if (menu) Menu.setApplicationMenu(menu);
|
||||
return null;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:AppState) => {
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const whenClauseContext = stateToWhenClauseContext(state);
|
||||
|
||||
return {
|
||||
|
|
|
@ -6,16 +6,16 @@ const { buildStyle } = require('@joplin/lib/theme');
|
|||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
interface MultiNoteActionsProps {
|
||||
themeId: number,
|
||||
selectedNoteIds: string[],
|
||||
notes: any[],
|
||||
dispatch: Function,
|
||||
watchedNoteFiles: string[],
|
||||
plugins: PluginStates,
|
||||
themeId: number;
|
||||
selectedNoteIds: string[];
|
||||
notes: any[];
|
||||
dispatch: Function;
|
||||
watchedNoteFiles: string[];
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
function styles_(props:MultiNoteActionsProps) {
|
||||
return buildStyle('MultiNoteActions', props.themeId, (theme:any) => {
|
||||
function styles_(props: MultiNoteActionsProps) {
|
||||
return buildStyle('MultiNoteActions', props.themeId, (theme: any) => {
|
||||
return {
|
||||
root: {
|
||||
display: 'inline-flex',
|
||||
|
@ -35,10 +35,10 @@ function styles_(props:MultiNoteActionsProps) {
|
|||
});
|
||||
}
|
||||
|
||||
export default function MultiNoteActions(props:MultiNoteActionsProps) {
|
||||
export default function MultiNoteActions(props: MultiNoteActionsProps) {
|
||||
const styles = styles_(props);
|
||||
|
||||
const multiNotesButton_click = (item:any) => {
|
||||
const multiNotesButton_click = (item: any) => {
|
||||
if (item.submenu) {
|
||||
item.submenu.popup(bridge().window());
|
||||
} else {
|
||||
|
|
|
@ -7,10 +7,10 @@ const Countable = require('countable');
|
|||
const markupLanguageUtils = require('@joplin/lib/markupLanguageUtils').default;
|
||||
|
||||
interface NoteContentPropertiesDialogProps {
|
||||
themeId: number,
|
||||
text: string,
|
||||
markupLanguage: number,
|
||||
onClose: Function,
|
||||
themeId: number;
|
||||
text: string;
|
||||
markupLanguage: number;
|
||||
onClose: Function;
|
||||
}
|
||||
|
||||
interface TextPropertiesMap {
|
||||
|
@ -21,15 +21,15 @@ interface KeyToLabelMap {
|
|||
[key: string]: string;
|
||||
}
|
||||
|
||||
let markupToHtml_:any = null;
|
||||
let markupToHtml_: any = null;
|
||||
function markupToHtml() {
|
||||
if (markupToHtml_) return markupToHtml_;
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
|
||||
return markupToHtml_;
|
||||
}
|
||||
|
||||
function countElements(text:string, wordSetter:Function, characterSetter:Function, characterNoSpaceSetter:Function, lineSetter:Function) {
|
||||
Countable.count(text, (counter:any) => {
|
||||
function countElements(text: string, wordSetter: Function, characterSetter: Function, characterNoSpaceSetter: Function, lineSetter: Function) {
|
||||
Countable.count(text, (counter: any) => {
|
||||
wordSetter(counter.words);
|
||||
characterSetter(counter.all);
|
||||
characterNoSpaceSetter(counter.characters);
|
||||
|
@ -45,7 +45,7 @@ function formatReadTime(readTimeMinutes: number) {
|
|||
return Math.ceil(readTimeMinutes).toString();
|
||||
}
|
||||
|
||||
export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) {
|
||||
export default function NoteContentPropertiesDialog(props: NoteContentPropertiesDialogProps) {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const tableBodyComps: any[] = [];
|
||||
// For the source Markdown
|
||||
|
|
|
@ -100,7 +100,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
|||
resetScroll: () => {
|
||||
resetScroll();
|
||||
},
|
||||
scrollTo: (options:ScrollOptions) => {
|
||||
scrollTo: (options: ScrollOptions) => {
|
||||
if (options.type === ScrollOptionTypes.Hash) {
|
||||
if (!webviewRef.current) return;
|
||||
webviewRef.current.wrappedInstance.send('scrollToHash', options.value as string);
|
||||
|
@ -156,7 +156,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
|||
selectedHtml: () => {
|
||||
return selectedText();
|
||||
},
|
||||
replaceSelection: (value:any) => {
|
||||
replaceSelection: (value: any) => {
|
||||
return editorRef.current.replaceSelection(value);
|
||||
},
|
||||
textBold: () => wrapSelectionWithStrings('**', '**', _('strong text')),
|
||||
|
@ -293,9 +293,9 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
|||
menu.popup(bridge().window());
|
||||
}, [props.content, editorCutText, editorPasteText, editorCopyText, onEditorPaste]);
|
||||
|
||||
const loadScript = async (script:any) => {
|
||||
const loadScript = async (script: any) => {
|
||||
return new Promise((resolve) => {
|
||||
let element:any = document.createElement('script');
|
||||
let element: any = document.createElement('script');
|
||||
if (script.src.indexOf('.css') >= 0) {
|
||||
element = document.createElement('link');
|
||||
element.rel = 'stylesheet';
|
||||
|
@ -324,7 +324,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
|||
let cancelled = false;
|
||||
|
||||
async function loadScripts() {
|
||||
const scriptsToLoad:{src: string, id:string, loaded: boolean}[] = [
|
||||
const scriptsToLoad: {src: string; id: string; loaded: boolean}[] = [
|
||||
{
|
||||
src: 'node_modules/codemirror/addon/dialog/dialog.css',
|
||||
id: 'codemirrorDialogStyle',
|
||||
|
|
|
@ -75,18 +75,18 @@ for (let i = 0; i < topLanguages.length; i++) {
|
|||
}
|
||||
|
||||
export interface EditorProps {
|
||||
value: string,
|
||||
searchMarkers: any,
|
||||
mode: string,
|
||||
style: any,
|
||||
codeMirrorTheme: any,
|
||||
readOnly: boolean,
|
||||
autoMatchBraces: boolean,
|
||||
keyMap: string,
|
||||
onChange: any,
|
||||
onScroll: any,
|
||||
onEditorContextMenu: any,
|
||||
onEditorPaste: any,
|
||||
value: string;
|
||||
searchMarkers: any;
|
||||
mode: string;
|
||||
style: any;
|
||||
codeMirrorTheme: any;
|
||||
readOnly: boolean;
|
||||
autoMatchBraces: boolean;
|
||||
keyMap: string;
|
||||
onChange: any;
|
||||
onScroll: any;
|
||||
onEditorContextMenu: any;
|
||||
onEditorPaste: any;
|
||||
}
|
||||
|
||||
function Editor(props: EditorProps, ref: any) {
|
||||
|
|
|
@ -9,11 +9,11 @@ import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenC
|
|||
const { buildStyle } = require('@joplin/lib/theme');
|
||||
|
||||
interface ToolbarProps {
|
||||
themeId: number,
|
||||
toolbarButtonInfos: ToolbarButtonInfo[],
|
||||
themeId: number;
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
}
|
||||
|
||||
function styles_(props:ToolbarProps) {
|
||||
function styles_(props: ToolbarProps) {
|
||||
return buildStyle('CodeMirrorToolbar', props.themeId, () => {
|
||||
return {
|
||||
root: {
|
||||
|
@ -26,7 +26,7 @@ function styles_(props:ToolbarProps) {
|
|||
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
|
||||
|
||||
function Toolbar(props:ToolbarProps) {
|
||||
function Toolbar(props: ToolbarProps) {
|
||||
const styles = styles_(props);
|
||||
return <ToolbarBase style={styles.root} items={props.toolbarButtonInfos} />;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ export function useScrollHandler(editorRef: any, webviewRef: any, onScroll: Func
|
|||
}
|
||||
|
||||
|
||||
export function useRootSize(dependencies:any) {
|
||||
export function useRootSize(dependencies: any) {
|
||||
const { rootRef } = dependencies;
|
||||
|
||||
const [rootSize, setRootSize] = useState({ width: 0, height: 0 });
|
||||
|
|
|
@ -34,7 +34,7 @@ export default function useJoplinMode(CodeMirror: any) {
|
|||
}
|
||||
|
||||
return {
|
||||
startState: function(): { outer: any, openCharacter: string, inner: any } {
|
||||
startState: function(): { outer: any; openCharacter: string; inner: any } {
|
||||
return {
|
||||
outer: CodeMirror.startState(markdownMode),
|
||||
openCharacter: '',
|
||||
|
|
|
@ -22,7 +22,7 @@ const { themeStyle } = require('@joplin/lib/theme');
|
|||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
|
||||
function markupRenderOptions(override:any = null) {
|
||||
function markupRenderOptions(override: any = null) {
|
||||
return {
|
||||
plugins: {
|
||||
checkbox: {
|
||||
|
@ -37,7 +37,7 @@ function markupRenderOptions(override:any = null) {
|
|||
};
|
||||
}
|
||||
|
||||
function findBlockSource(node:any) {
|
||||
function findBlockSource(node: any) {
|
||||
const sources = node.getElementsByClassName('joplin-source');
|
||||
if (!sources.length) throw new Error('No source for node');
|
||||
const source = sources[0];
|
||||
|
@ -51,7 +51,7 @@ function findBlockSource(node:any) {
|
|||
};
|
||||
}
|
||||
|
||||
function newBlockSource(language:string = '', content:string = ''):any {
|
||||
function newBlockSource(language: string = '', content: string = ''): any {
|
||||
const fence = language === 'katex' ? '$$' : '```';
|
||||
const fenceLanguage = language === 'katex' ? '' : language;
|
||||
|
||||
|
@ -64,7 +64,7 @@ function newBlockSource(language:string = '', content:string = ''):any {
|
|||
};
|
||||
}
|
||||
|
||||
function findEditableContainer(node:any):any {
|
||||
function findEditableContainer(node: any): any {
|
||||
while (node) {
|
||||
if (node.classList && node.classList.contains('joplin-editable')) return node;
|
||||
node = node.parentNode;
|
||||
|
@ -72,7 +72,7 @@ function findEditableContainer(node:any):any {
|
|||
return null;
|
||||
}
|
||||
|
||||
function editableInnerHtml(html:string):string {
|
||||
function editableInnerHtml(html: string): string {
|
||||
const temp = document.createElement('div');
|
||||
temp.innerHTML = html;
|
||||
const editable = temp.getElementsByClassName('joplin-editable');
|
||||
|
@ -80,14 +80,14 @@ function editableInnerHtml(html:string):string {
|
|||
return editable[0].innerHTML;
|
||||
}
|
||||
|
||||
function dialogTextArea_keyDown(event:any) {
|
||||
function dialogTextArea_keyDown(event: any) {
|
||||
if (event.key === 'Tab') {
|
||||
window.requestAnimationFrame(() => event.target.focus());
|
||||
}
|
||||
}
|
||||
|
||||
let markupToHtml_ = new MarkupToHtml();
|
||||
function stripMarkup(markupLanguage:number, markup:string, options:any = null) {
|
||||
function stripMarkup(markupLanguage: number, markup: string, options: any = null) {
|
||||
if (!markupToHtml_) markupToHtml_ = new MarkupToHtml();
|
||||
return markupToHtml_.stripMarkup(markupLanguage, markup, options);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ function stripMarkup(markupLanguage:number, markup:string, options:any = null) {
|
|||
// Allows pressing tab in a textarea to input an actual tab (instead of changing focus)
|
||||
// taboverride will take care of actually inserting the tab character, while the keydown
|
||||
// event listener will override the default behaviour, which is to focus the next field.
|
||||
function enableTextAreaTab(enable:boolean) {
|
||||
function enableTextAreaTab(enable: boolean) {
|
||||
const textAreas = document.getElementsByClassName('tox-textarea');
|
||||
for (const textArea of textAreas) {
|
||||
taboverride.set(textArea, enable);
|
||||
|
@ -109,28 +109,28 @@ function enableTextAreaTab(enable:boolean) {
|
|||
}
|
||||
|
||||
interface TinyMceCommand {
|
||||
name: string,
|
||||
value?: any,
|
||||
ui?: boolean
|
||||
name: string;
|
||||
value?: any;
|
||||
ui?: boolean;
|
||||
}
|
||||
|
||||
interface JoplinCommandToTinyMceCommands {
|
||||
[key:string]: TinyMceCommand,
|
||||
[key: string]: TinyMceCommand;
|
||||
}
|
||||
|
||||
const joplinCommandToTinyMceCommands:JoplinCommandToTinyMceCommands = {
|
||||
const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
|
||||
'textBold': { name: 'mceToggleFormat', value: 'bold' },
|
||||
'textItalic': { name: 'mceToggleFormat', value: 'italic' },
|
||||
'textLink': { name: 'mceLink' },
|
||||
'search': { name: 'SearchReplace' },
|
||||
};
|
||||
|
||||
let loadedCssFiles_:string[] = [];
|
||||
let loadedJsFiles_:string[] = [];
|
||||
let dispatchDidUpdateIID_:any = null;
|
||||
let changeId_:number = 1;
|
||||
let loadedCssFiles_: string[] = [];
|
||||
let loadedJsFiles_: string[] = [];
|
||||
let dispatchDidUpdateIID_: any = null;
|
||||
let changeId_: number = 1;
|
||||
|
||||
const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const [editor, setEditor] = useState(null);
|
||||
const [scriptLoaded, setScriptLoaded] = useState(false);
|
||||
const [editorReady, setEditorReady] = useState(false);
|
||||
|
@ -162,7 +162,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
|
||||
usePluginServiceRegistration(ref);
|
||||
|
||||
const dispatchDidUpdate = (editor:any) => {
|
||||
const dispatchDidUpdate = (editor: any) => {
|
||||
if (dispatchDidUpdateIID_) shim.clearTimeout(dispatchDidUpdateIID_);
|
||||
dispatchDidUpdateIID_ = shim.setTimeout(() => {
|
||||
dispatchDidUpdateIID_ = null;
|
||||
|
@ -170,7 +170,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
}, 10);
|
||||
};
|
||||
|
||||
const insertResourcesIntoContent = useCallback(async (filePaths:string[] = null, options:any = null) => {
|
||||
const insertResourcesIntoContent = useCallback(async (filePaths: string[] = null, options: any = null) => {
|
||||
const resourceMd = await commandAttachFileToBody('', filePaths, options);
|
||||
if (!resourceMd) return;
|
||||
const result = await props.markupToHtml(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMd, markupRenderOptions({ bodyOnly: true }));
|
||||
|
@ -182,7 +182,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
const insertResourcesIntoContentRef = useRef(null);
|
||||
insertResourcesIntoContentRef.current = insertResourcesIntoContent;
|
||||
|
||||
const onEditorContentClick = useCallback((event:any) => {
|
||||
const onEditorContentClick = useCallback((event: any) => {
|
||||
const nodeName = event.target ? event.target.nodeName : '';
|
||||
|
||||
if (nodeName === 'INPUT' && event.target.getAttribute('type') === 'checkbox') {
|
||||
|
@ -216,7 +216,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
resetScroll: () => {
|
||||
if (editor) editor.getWin().scrollTo(0,0);
|
||||
},
|
||||
scrollTo: (options:ScrollOptions) => {
|
||||
scrollTo: (options: ScrollOptions) => {
|
||||
if (!editor) return;
|
||||
|
||||
if (options.type === ScrollOptionTypes.Hash) {
|
||||
|
@ -232,11 +232,11 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
throw new Error(`Unsupported scroll options: ${options.type}`);
|
||||
}
|
||||
},
|
||||
supportsCommand: (name:string) => {
|
||||
supportsCommand: (name: string) => {
|
||||
// TODO: should also handle commands that are not in this map (insertText, focus, etc);
|
||||
return !!joplinCommandToTinyMceCommands[name];
|
||||
},
|
||||
execCommand: async (cmd:EditorCommand) => {
|
||||
execCommand: async (cmd: EditorCommand) => {
|
||||
if (!editor) return false;
|
||||
|
||||
reg.logger().debug('TinyMce: execCommand', cmd);
|
||||
|
@ -263,14 +263,14 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
|
||||
if (commandProcessed) return true;
|
||||
|
||||
const additionalCommands:any = {
|
||||
const additionalCommands: any = {
|
||||
selectedText: () => {
|
||||
return stripMarkup(MarkupToHtml.MARKUP_LANGUAGE_HTML, editor.selection.getContent());
|
||||
},
|
||||
selectedHtml: () => {
|
||||
return editor.selection.getContent();
|
||||
},
|
||||
replaceSelection: (value:any) => {
|
||||
replaceSelection: (value: any) => {
|
||||
editor.selection.setContent(value);
|
||||
},
|
||||
};
|
||||
|
@ -284,7 +284,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
return false;
|
||||
}
|
||||
|
||||
const tinyMceCmd:TinyMceCommand = { ...joplinCommandToTinyMceCommands[cmd.name] };
|
||||
const tinyMceCmd: TinyMceCommand = { ...joplinCommandToTinyMceCommands[cmd.name] };
|
||||
if (!('ui' in tinyMceCmd)) tinyMceCmd.ui = false;
|
||||
if (!('value' in tinyMceCmd)) tinyMceCmd.value = null;
|
||||
|
||||
|
@ -301,9 +301,9 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
// module would not load these extra files.
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
const loadScript = async (script:any) => {
|
||||
const loadScript = async (script: any) => {
|
||||
return new Promise((resolve) => {
|
||||
let element:any = document.createElement('script');
|
||||
let element: any = document.createElement('script');
|
||||
if (script.src.indexOf('.css') >= 0) {
|
||||
element = document.createElement('link');
|
||||
element.rel = 'stylesheet';
|
||||
|
@ -332,7 +332,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
let cancelled = false;
|
||||
|
||||
async function loadScripts() {
|
||||
const scriptsToLoad:any[] = [
|
||||
const scriptsToLoad: any[] = [
|
||||
{
|
||||
src: 'node_modules/tinymce/tinymce.min.js',
|
||||
id: 'tinyMceScript',
|
||||
|
@ -510,7 +510,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
const loadEditor = async () => {
|
||||
const language = closestSupportedLocale(props.locale, true, supportedLocales);
|
||||
|
||||
const pluginCommandNames:string[] = [];
|
||||
const pluginCommandNames: string[] = [];
|
||||
|
||||
const infos = pluginUtils.viewInfosByType(props.plugins, 'toolbarButton');
|
||||
|
||||
|
@ -551,9 +551,9 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
localization_function: _,
|
||||
contextmenu: false,
|
||||
browser_spellcheck: true,
|
||||
setup: (editor:any) => {
|
||||
setup: (editor: any) => {
|
||||
|
||||
function openEditDialog(editable:any) {
|
||||
function openEditDialog(editable: any) {
|
||||
const source = editable ? findBlockSource(editable) : newBlockSource();
|
||||
|
||||
editor.windowManager.open({
|
||||
|
@ -563,7 +563,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
codeTextArea: source.content,
|
||||
languageInput: source.language,
|
||||
},
|
||||
onSubmit: async (dialogApi:any) => {
|
||||
onSubmit: async (dialogApi: any) => {
|
||||
const newSource = newBlockSource(dialogApi.getData().languageInput, dialogApi.getData().codeTextArea);
|
||||
const md = `${newSource.openCharacters}${newSource.content.trim()}${newSource.closeCharacters}`;
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, md, { bodyOnly: true });
|
||||
|
@ -638,7 +638,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
onAction: function() {
|
||||
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' });
|
||||
},
|
||||
onSetup: function(api:any) {
|
||||
onSetup: function(api: any) {
|
||||
api.setActive(editor.formatter.match('code'));
|
||||
const unbind = editor.formatter.formatChanged('code', api.setActive).unbind;
|
||||
|
||||
|
@ -669,17 +669,17 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
setupContextMenu(editor);
|
||||
|
||||
// TODO: remove event on unmount?
|
||||
editor.on('DblClick', (event:any) => {
|
||||
editor.on('DblClick', (event: any) => {
|
||||
const editable = findEditableContainer(event.target);
|
||||
if (editable) openEditDialog(editable);
|
||||
});
|
||||
|
||||
// This is triggered when an external file is dropped on the editor
|
||||
editor.on('drop', (event:any) => {
|
||||
editor.on('drop', (event: any) => {
|
||||
props_onDrop.current(event);
|
||||
});
|
||||
|
||||
editor.on('ObjectResized', function(event:any) {
|
||||
editor.on('ObjectResized', function(event: any) {
|
||||
if (event.target.nodeName === 'IMG') {
|
||||
editor.fire('joplinChange');
|
||||
dispatchDidUpdate(editor);
|
||||
|
@ -706,7 +706,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
// Set the initial content and load the plugin CSS and JS files
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
const loadDocumentAssets = (editor:any, pluginAssets:any[]) => {
|
||||
const loadDocumentAssets = (editor: any, pluginAssets: any[]) => {
|
||||
// Note: The way files are cached is not correct because it assumes there's only one version
|
||||
// of each file. However, when the theme change, a new CSS file, specific to the theme, is
|
||||
// created. That file should not be loaded on top of the previous one, but as a replacement.
|
||||
|
@ -720,7 +720,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
let docHead_:any = null;
|
||||
let docHead_: any = null;
|
||||
|
||||
function docHead() {
|
||||
if (docHead_) return docHead_;
|
||||
|
@ -733,15 +733,15 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
`gui/note-viewer/pluginAssets/highlight.js/${theme.codeThemeCss}`,
|
||||
].concat(
|
||||
pluginAssets
|
||||
.filter((a:any) => a.mime === 'text/css')
|
||||
.map((a:any) => a.path)
|
||||
).filter((path:string) => !loadedCssFiles_.includes(path));
|
||||
.filter((a: any) => a.mime === 'text/css')
|
||||
.map((a: any) => a.path)
|
||||
).filter((path: string) => !loadedCssFiles_.includes(path));
|
||||
|
||||
const jsFiles = [].concat(
|
||||
pluginAssets
|
||||
.filter((a:any) => a.mime === 'application/javascript')
|
||||
.map((a:any) => a.path)
|
||||
).filter((path:string) => !loadedJsFiles_.includes(path));
|
||||
.filter((a: any) => a.mime === 'application/javascript')
|
||||
.map((a: any) => a.path)
|
||||
).filter((path: string) => !loadedJsFiles_.includes(path));
|
||||
|
||||
for (const cssFile of cssFiles) loadedCssFiles_.push(cssFile);
|
||||
for (const jsFile of jsFiles) loadedJsFiles_.push(jsFile);
|
||||
|
@ -937,8 +937,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
function onExecCommand(event:any) {
|
||||
const c:string = event.command;
|
||||
function onExecCommand(event: any) {
|
||||
const c: string = event.command;
|
||||
if (!c) return;
|
||||
|
||||
// We need to dispatch onChange for these commands:
|
||||
|
@ -972,13 +972,13 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
// onChange even though nothing is changed. The alternative would be to
|
||||
// check the content before and after, but this is too slow, so let's
|
||||
// keep it this way for now.
|
||||
function onKeyUp(event:any) {
|
||||
function onKeyUp(event: any) {
|
||||
if (['Backspace', 'Delete', 'Enter', 'Tab'].includes(event.key)) {
|
||||
onChangeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
async function onPaste(event:any) {
|
||||
async function onPaste(event: any) {
|
||||
const resourceMds = await handlePasteEvent(event);
|
||||
if (resourceMds.length) {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
|
@ -1000,7 +1000,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
}
|
||||
}
|
||||
|
||||
function onKeyDown(event:any) {
|
||||
function onKeyDown(event: any) {
|
||||
// Handle "paste as text". Note that when pressing CtrlOrCmd+Shift+V it's going
|
||||
// to trigger the "keydown" event but not the "paste" event, so it's ok to process
|
||||
// it here and we don't need to do anything special in onPaste
|
||||
|
@ -1053,7 +1053,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
function renderExtraToolbarButton(key:string, info:ToolbarButtonInfo) {
|
||||
function renderExtraToolbarButton(key: string, info: ToolbarButtonInfo) {
|
||||
return <ToolbarButton
|
||||
key={key}
|
||||
themeId={props.themeId}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { NoteBodyEditorProps } from '../../../utils/types';
|
||||
const { buildStyle } = require('@joplin/lib/theme');
|
||||
|
||||
export default function styles(props:NoteBodyEditorProps) {
|
||||
return buildStyle(['TinyMCE', props.style.width, props.style.height], props.themeId, (theme:any) => {
|
||||
export default function styles(props: NoteBodyEditorProps) {
|
||||
return buildStyle(['TinyMCE', props.style.width, props.style.height], props.themeId, (theme: any) => {
|
||||
const extraToolbarContainer = {
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
display: 'flex',
|
||||
|
|
|
@ -6,7 +6,7 @@ const Resource = require('@joplin/lib/models/Resource');
|
|||
// x and y are the absolute coordinates, as returned by the context-menu event
|
||||
// handler on the webContent. This function will return null if the point is
|
||||
// not within the TinyMCE editor.
|
||||
function contextMenuElement(editor:any, x:number, y:number) {
|
||||
function contextMenuElement(editor: any, x: number, y: number) {
|
||||
if (!editor || !editor.getDoc()) return null;
|
||||
|
||||
const iframes = document.getElementsByClassName('tox-edit-area__iframe');
|
||||
|
@ -24,19 +24,19 @@ function contextMenuElement(editor:any, x:number, y:number) {
|
|||
}
|
||||
|
||||
interface ContextMenuActionOptions {
|
||||
current: ContextMenuOptions,
|
||||
current: ContextMenuOptions;
|
||||
}
|
||||
|
||||
const contextMenuActionOptions:ContextMenuActionOptions = { current: null };
|
||||
const contextMenuActionOptions: ContextMenuActionOptions = { current: null };
|
||||
|
||||
export default function(editor:any) {
|
||||
export default function(editor: any) {
|
||||
const contextMenuItems = menuItems();
|
||||
|
||||
bridge().window().webContents.on('context-menu', (_event:any, params:any) => {
|
||||
bridge().window().webContents.on('context-menu', (_event: any, params: any) => {
|
||||
const element = contextMenuElement(editor, params.x, params.y);
|
||||
if (!element) return;
|
||||
|
||||
let itemType:ContextMenuItemType = ContextMenuItemType.None;
|
||||
let itemType: ContextMenuItemType = ContextMenuItemType.None;
|
||||
let resourceId = '';
|
||||
let linkToCopy = null;
|
||||
|
||||
|
@ -57,7 +57,7 @@ export default function(editor:any) {
|
|||
linkToCopy,
|
||||
textToCopy: null,
|
||||
htmlToCopy: editor.selection ? editor.selection.getContent() : '',
|
||||
insertContent: (content:string) => {
|
||||
insertContent: (content: string) => {
|
||||
editor.insertContent(content);
|
||||
},
|
||||
isReadOnly: false,
|
||||
|
|
|
@ -2,11 +2,11 @@ import { useEffect, useCallback, useRef } from 'react';
|
|||
import shim from '@joplin/lib/shim';
|
||||
|
||||
interface HookDependencies {
|
||||
editor:any,
|
||||
onScroll: Function,
|
||||
editor: any;
|
||||
onScroll: Function;
|
||||
}
|
||||
|
||||
export default function useScroll(dependencies:HookDependencies) {
|
||||
export default function useScroll(dependencies: HookDependencies) {
|
||||
const { editor, onScroll } = dependencies;
|
||||
const scrollTimeoutId_ = useRef(null);
|
||||
|
||||
|
@ -37,7 +37,7 @@ export default function useScroll(dependencies:HookDependencies) {
|
|||
return m <= 0 ? 0 : t / m;
|
||||
}, [maxScrollTop, scrollTop]);
|
||||
|
||||
const scrollToPercent = useCallback((percent:number) => {
|
||||
const scrollToPercent = useCallback((percent: number) => {
|
||||
if (!editor) return;
|
||||
editor.getWin().scrollTo(0, maxScrollTop() * percent);
|
||||
}, [editor, maxScrollTop]);
|
||||
|
|
|
@ -55,7 +55,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||
const isMountedRef = useRef(true);
|
||||
const noteSearchBarRef = useRef(null);
|
||||
|
||||
const formNote_beforeLoad = useCallback(async (event:OnLoadEvent) => {
|
||||
const formNote_beforeLoad = useCallback(async (event: OnLoadEvent) => {
|
||||
await saveNoteIfWillChange(event.formNote);
|
||||
setShowRevisions(false);
|
||||
}, []);
|
||||
|
@ -106,7 +106,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||
return async function() {
|
||||
const note = await formNoteToNote(formNote);
|
||||
reg.logger().debug('Saving note...', note);
|
||||
const savedNote:any = await Note.save(note);
|
||||
const savedNote: any = await Note.save(note);
|
||||
|
||||
setFormNote((prev: FormNote) => {
|
||||
return { ...prev, user_updated_time: savedNote.user_updated_time };
|
||||
|
@ -334,7 +334,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||
});
|
||||
}, [props.dispatch, formNote]);
|
||||
|
||||
function renderNoNotes(rootStyle:any) {
|
||||
function renderNoNotes(rootStyle: any) {
|
||||
const emptyDivStyle = Object.assign(
|
||||
{
|
||||
backgroundColor: 'black',
|
||||
|
@ -365,7 +365,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||
|
||||
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
|
||||
|
||||
const editorProps:NoteBodyEditorProps = {
|
||||
const editorProps: NoteBodyEditorProps = {
|
||||
ref: editorRef,
|
||||
contentKey: formNote.id,
|
||||
style: styles.tinyMCE,
|
||||
|
@ -417,7 +417,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||
if (showRevisions) {
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const revStyle:any = {
|
||||
const revStyle: any = {
|
||||
// ...props.style,
|
||||
display: 'inline-flex',
|
||||
padding: theme.margin,
|
||||
|
|
|
@ -7,13 +7,13 @@ import { buildStyle } from '@joplin/lib/theme';
|
|||
import time from '@joplin/lib/time';
|
||||
|
||||
interface Props {
|
||||
themeId: number,
|
||||
noteUserUpdatedTime: number,
|
||||
noteTitle: string,
|
||||
noteIsTodo: number,
|
||||
isProvisional: boolean,
|
||||
titleInputRef: any,
|
||||
onTitleChange(event: ChangeEvent<HTMLInputElement>):void,
|
||||
themeId: number;
|
||||
noteUserUpdatedTime: number;
|
||||
noteTitle: string;
|
||||
noteIsTodo: number;
|
||||
isProvisional: boolean;
|
||||
titleInputRef: any;
|
||||
onTitleChange(event: ChangeEvent<HTMLInputElement>): void;
|
||||
}
|
||||
|
||||
function styles_(props: Props) {
|
||||
|
@ -52,10 +52,10 @@ function styles_(props: Props) {
|
|||
});
|
||||
}
|
||||
|
||||
export default function NoteTitleBar(props:Props) {
|
||||
export default function NoteTitleBar(props: Props) {
|
||||
const styles = styles_(props);
|
||||
|
||||
const onTitleKeydown = useCallback((event:any) => {
|
||||
const onTitleKeydown = useCallback((event: any) => {
|
||||
const keyCode = event.keyCode;
|
||||
|
||||
if (keyCode === 9) { // TAB
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
const declarations:CommandDeclaration[] = [
|
||||
const declarations: CommandDeclaration[] = [
|
||||
{
|
||||
name: 'insertText',
|
||||
},
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementNoteBody',
|
||||
label: () => _('Note body'),
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.editorRef.current.execCommand({ name: 'focus' });
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementNoteTitle',
|
||||
label: () => _('Note title'),
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
if (!comp.titleInputRef.current) return;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showLocalSearch',
|
||||
label: () => _('Search in current note'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
if (comp.editorRef.current && comp.editorRef.current.supportsCommand('search')) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'showRevisions',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.setShowRevisions(true);
|
||||
|
|
|
@ -18,32 +18,32 @@ export enum ContextMenuItemType {
|
|||
}
|
||||
|
||||
export interface ContextMenuOptions {
|
||||
itemType: ContextMenuItemType,
|
||||
resourceId: string,
|
||||
linkToCopy: string,
|
||||
textToCopy: string,
|
||||
htmlToCopy: string,
|
||||
insertContent: Function,
|
||||
isReadOnly?: boolean,
|
||||
itemType: ContextMenuItemType;
|
||||
resourceId: string;
|
||||
linkToCopy: string;
|
||||
textToCopy: string;
|
||||
htmlToCopy: string;
|
||||
insertContent: Function;
|
||||
isReadOnly?: boolean;
|
||||
}
|
||||
|
||||
interface ContextMenuItem {
|
||||
label: string,
|
||||
onAction: Function,
|
||||
isActive: Function,
|
||||
label: string;
|
||||
onAction: Function;
|
||||
isActive: Function;
|
||||
}
|
||||
|
||||
interface ContextMenuItems {
|
||||
[key:string]: ContextMenuItem;
|
||||
[key: string]: ContextMenuItem;
|
||||
}
|
||||
|
||||
async function resourceInfo(options:ContextMenuOptions):Promise<any> {
|
||||
async function resourceInfo(options: ContextMenuOptions): Promise<any> {
|
||||
const resource = options.resourceId ? await Resource.load(options.resourceId) : null;
|
||||
const resourcePath = resource ? Resource.fullPath(resource) : '';
|
||||
return { resource, resourcePath };
|
||||
}
|
||||
|
||||
function handleCopyToClipboard(options:ContextMenuOptions) {
|
||||
function handleCopyToClipboard(options: ContextMenuOptions) {
|
||||
if (options.textToCopy) {
|
||||
clipboard.writeText(options.textToCopy);
|
||||
} else if (options.htmlToCopy) {
|
||||
|
@ -51,11 +51,11 @@ function handleCopyToClipboard(options:ContextMenuOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
export function menuItems():ContextMenuItems {
|
||||
export function menuItems(): ContextMenuItems {
|
||||
return {
|
||||
open: {
|
||||
label: _('Open...'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
try {
|
||||
await ResourceEditWatcher.instance().openAndWatch(options.resourceId);
|
||||
} catch (error) {
|
||||
|
@ -63,11 +63,11 @@ export function menuItems():ContextMenuItems {
|
|||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
},
|
||||
isActive: (itemType:ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
isActive: (itemType: ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
},
|
||||
saveAs: {
|
||||
label: _('Save as...'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
const { resourcePath, resource } = await resourceInfo(options);
|
||||
const filePath = bridge().showSaveDialog({
|
||||
defaultPath: resource.filename ? resource.filename : resource.title,
|
||||
|
@ -75,58 +75,58 @@ export function menuItems():ContextMenuItems {
|
|||
if (!filePath) return;
|
||||
await fs.copy(resourcePath, filePath);
|
||||
},
|
||||
isActive: (itemType:ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
isActive: (itemType: ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
},
|
||||
revealInFolder: {
|
||||
label: _('Reveal file in folder'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
const { resourcePath } = await resourceInfo(options);
|
||||
bridge().showItemInFolder(resourcePath);
|
||||
},
|
||||
isActive: (itemType:ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
isActive: (itemType: ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
},
|
||||
copyPathToClipboard: {
|
||||
label: _('Copy path to clipboard'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
const { resourcePath } = await resourceInfo(options);
|
||||
clipboard.writeText(toSystemSlashes(resourcePath));
|
||||
},
|
||||
isActive: (itemType:ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
isActive: (itemType: ContextMenuItemType) => itemType === ContextMenuItemType.Image || itemType === ContextMenuItemType.Resource,
|
||||
},
|
||||
cut: {
|
||||
label: _('Cut'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
handleCopyToClipboard(options);
|
||||
options.insertContent('');
|
||||
},
|
||||
isActive: (_itemType:ContextMenuItemType, options:ContextMenuOptions) => !options.isReadOnly && (!!options.textToCopy || !!options.htmlToCopy),
|
||||
isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.isReadOnly && (!!options.textToCopy || !!options.htmlToCopy),
|
||||
},
|
||||
copy: {
|
||||
label: _('Copy'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
handleCopyToClipboard(options);
|
||||
},
|
||||
isActive: (_itemType:ContextMenuItemType, options:ContextMenuOptions) => !!options.textToCopy || !!options.htmlToCopy,
|
||||
isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !!options.textToCopy || !!options.htmlToCopy,
|
||||
},
|
||||
paste: {
|
||||
label: _('Paste'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
const content = clipboard.readHTML() ? clipboard.readHTML() : clipboard.readText();
|
||||
options.insertContent(content);
|
||||
},
|
||||
isActive: (_itemType:ContextMenuItemType, options:ContextMenuOptions) => !options.isReadOnly && (!!clipboard.readText() || !!clipboard.readHTML()),
|
||||
isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.isReadOnly && (!!clipboard.readText() || !!clipboard.readHTML()),
|
||||
},
|
||||
copyLinkUrl: {
|
||||
label: _('Copy Link Address'),
|
||||
onAction: async (options:ContextMenuOptions) => {
|
||||
onAction: async (options: ContextMenuOptions) => {
|
||||
clipboard.writeText(options.linkToCopy !== null ? options.linkToCopy : options.textToCopy);
|
||||
},
|
||||
isActive: (itemType:ContextMenuItemType, options:ContextMenuOptions) => itemType === ContextMenuItemType.Link || !!options.linkToCopy,
|
||||
isActive: (itemType: ContextMenuItemType, options: ContextMenuOptions) => itemType === ContextMenuItemType.Link || !!options.linkToCopy,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function contextMenu(options:ContextMenuOptions) {
|
||||
export default async function contextMenu(options: ContextMenuOptions) {
|
||||
const menu = new Menu();
|
||||
|
||||
const items = menuItems();
|
||||
|
|
|
@ -4,7 +4,7 @@ const HtmlToMd = require('@joplin/lib/HtmlToMd');
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
const { MarkupToHtml } = require('@joplin/renderer');
|
||||
|
||||
export async function htmlToMarkdown(markupLanguage: number, html: string, originalCss:string): Promise<string> {
|
||||
export async function htmlToMarkdown(markupLanguage: number, html: string, originalCss: string): Promise<string> {
|
||||
let newBody = '';
|
||||
|
||||
if (markupLanguage === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) {
|
||||
|
|
|
@ -52,7 +52,7 @@ export async function attachedResources(noteBody: string): Promise<any> {
|
|||
return output;
|
||||
}
|
||||
|
||||
export async function commandAttachFileToBody(body:string, filePaths:string[] = null, options:any = null) {
|
||||
export async function commandAttachFileToBody(body: string, filePaths: string[] = null, options: any = null) {
|
||||
options = {
|
||||
createFileURL: false,
|
||||
position: 0,
|
||||
|
@ -101,7 +101,7 @@ export function resourcesStatus(resourceInfos: any) {
|
|||
return joplinRendererUtils.resourceStatusName(lowestIndex);
|
||||
}
|
||||
|
||||
export async function handlePasteEvent(event:any) {
|
||||
export async function handlePasteEvent(event: any) {
|
||||
const output = [];
|
||||
const formats = clipboard.availableFormats();
|
||||
for (let i = 0; i < formats.length; i++) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUt
|
|||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
|
||||
export interface ToolbarButtonInfos {
|
||||
[key:string]: ToolbarButtonInfo;
|
||||
[key: string]: ToolbarButtonInfo;
|
||||
}
|
||||
|
||||
export interface NoteEditorProps {
|
||||
|
@ -24,25 +24,25 @@ export interface NoteEditorProps {
|
|||
selectedNoteTags: any[];
|
||||
lastEditorScrollPercents: any;
|
||||
selectedNoteHash: string;
|
||||
searches: any[],
|
||||
selectedSearchId: string,
|
||||
customCss: string,
|
||||
noteVisiblePanes: string[],
|
||||
watchedResources: any,
|
||||
highlightedWords: any[],
|
||||
plugins: PluginStates,
|
||||
toolbarButtonInfos: ToolbarButtonInfo[],
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo,
|
||||
searches: any[];
|
||||
selectedSearchId: string;
|
||||
customCss: string;
|
||||
noteVisiblePanes: string[];
|
||||
watchedResources: any;
|
||||
highlightedWords: any[];
|
||||
plugins: PluginStates;
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
}
|
||||
|
||||
export interface NoteBodyEditorProps {
|
||||
style: any;
|
||||
ref: any,
|
||||
ref: any;
|
||||
themeId: number;
|
||||
content: string,
|
||||
contentKey: string,
|
||||
contentMarkupLanguage: number,
|
||||
contentOriginalCss: string,
|
||||
content: string;
|
||||
contentKey: string;
|
||||
contentMarkupLanguage: number;
|
||||
contentOriginalCss: string;
|
||||
onChange(event: OnChangeEvent): void;
|
||||
onWillChange(event: any): void;
|
||||
onMessage(event: any): void;
|
||||
|
@ -53,29 +53,29 @@ export interface NoteBodyEditorProps {
|
|||
disabled: boolean;
|
||||
dispatch: Function;
|
||||
noteToolbar: any;
|
||||
setLocalSearchResultCount(count: number): void,
|
||||
searchMarkers: any,
|
||||
visiblePanes: string[],
|
||||
keyboardMode: string,
|
||||
resourceInfos: ResourceInfos,
|
||||
locale: string,
|
||||
onDrop: Function,
|
||||
noteToolbarButtonInfos: ToolbarButtonInfo[],
|
||||
plugins: PluginStates,
|
||||
setLocalSearchResultCount(count: number): void;
|
||||
searchMarkers: any;
|
||||
visiblePanes: string[];
|
||||
keyboardMode: string;
|
||||
resourceInfos: ResourceInfos;
|
||||
locale: string;
|
||||
onDrop: Function;
|
||||
noteToolbarButtonInfos: ToolbarButtonInfo[];
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
id: string,
|
||||
title: string,
|
||||
body: string,
|
||||
parent_id: string,
|
||||
is_todo: number,
|
||||
bodyEditorContent?: any,
|
||||
markup_language: number,
|
||||
user_updated_time: number,
|
||||
encryption_applied: number,
|
||||
id: string;
|
||||
title: string;
|
||||
body: string;
|
||||
parent_id: string;
|
||||
is_todo: number;
|
||||
bodyEditorContent?: any;
|
||||
markup_language: number;
|
||||
user_updated_time: number;
|
||||
encryption_applied: number;
|
||||
|
||||
hasChanged: boolean,
|
||||
hasChanged: boolean;
|
||||
|
||||
// Getting the content from the editor can be a slow process because that content
|
||||
// might need to be serialized first. For that reason, the wrapped editor (eg TinyMCE)
|
||||
|
@ -93,10 +93,10 @@ export interface FormNote {
|
|||
// types something then quickly switch a different note. In that case, bodyWillChangeId
|
||||
// is set, thus we know we should save the note, even though we won't receive the
|
||||
// onChange event.
|
||||
bodyWillChangeId: number
|
||||
bodyChangeId: number,
|
||||
bodyWillChangeId: number;
|
||||
bodyChangeId: number;
|
||||
|
||||
saveActionQueue: AsyncActionQueue,
|
||||
saveActionQueue: AsyncActionQueue;
|
||||
|
||||
// Note with markup_language = HTML have a block of CSS at the start, which is used
|
||||
// to preserve the style from the original (web-clipped) page. When sending the note
|
||||
|
@ -106,10 +106,10 @@ export interface FormNote {
|
|||
// Since the CSS used by TinyMCE has been lost (since it's in a temp CSS file), we keep that
|
||||
// original CSS here. It's used in formNoteToNote to rebuild the note body.
|
||||
// We can keep it here because we know TinyMCE will not modify it anyway.
|
||||
originalCss: string,
|
||||
originalCss: string;
|
||||
}
|
||||
|
||||
export function defaultFormNote():FormNote {
|
||||
export function defaultFormNote(): FormNote {
|
||||
return {
|
||||
id: '',
|
||||
parent_id: '',
|
||||
|
@ -128,12 +128,12 @@ export function defaultFormNote():FormNote {
|
|||
}
|
||||
|
||||
export interface ResourceInfo {
|
||||
localState: any,
|
||||
item: any,
|
||||
localState: any;
|
||||
item: any;
|
||||
}
|
||||
|
||||
export interface ResourceInfos {
|
||||
[index:string]: ResourceInfo,
|
||||
[index: string]: ResourceInfo;
|
||||
}
|
||||
|
||||
export enum ScrollOptionTypes {
|
||||
|
@ -143,8 +143,8 @@ export enum ScrollOptionTypes {
|
|||
}
|
||||
|
||||
export interface ScrollOptions {
|
||||
type: ScrollOptionTypes,
|
||||
value: any,
|
||||
type: ScrollOptionTypes;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface OnChangeEvent {
|
||||
|
|
|
@ -2,14 +2,15 @@ import { useCallback } from 'react';
|
|||
const Note = require('@joplin/lib/models/Note.js');
|
||||
|
||||
interface HookDependencies {
|
||||
editorRef:any,
|
||||
editorRef: any;
|
||||
}
|
||||
|
||||
export default function useDropHandler(dependencies:HookDependencies) {
|
||||
export default function useDropHandler(dependencies: HookDependencies) {
|
||||
const { editorRef } = dependencies;
|
||||
|
||||
return useCallback(async (event:any) => {
|
||||
return useCallback(async (event: any) => {
|
||||
const dt = event.dataTransfer;
|
||||
dt.dropEffect = 'copy';
|
||||
const createFileURL = event.altKey;
|
||||
|
||||
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||
|
|
|
@ -2,10 +2,10 @@ import { useState, useEffect } from 'react';
|
|||
const Folder = require('@joplin/lib/models/Folder');
|
||||
|
||||
interface HookDependencies {
|
||||
folderId: string,
|
||||
folderId: string;
|
||||
}
|
||||
|
||||
export default function(dependencies:HookDependencies) {
|
||||
export default function(dependencies: HookDependencies) {
|
||||
const { folderId } = dependencies;
|
||||
const [folder, setFolder] = useState(null);
|
||||
|
||||
|
|
|
@ -14,17 +14,17 @@ const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker.js');
|
|||
const ResourceEditWatcher = require('@joplin/lib/services/ResourceEditWatcher/index').default;
|
||||
|
||||
export interface OnLoadEvent {
|
||||
formNote: FormNote,
|
||||
formNote: FormNote;
|
||||
}
|
||||
|
||||
interface HookDependencies {
|
||||
syncStarted: boolean,
|
||||
noteId: string,
|
||||
isProvisional: boolean,
|
||||
titleInputRef: any,
|
||||
editorRef: any,
|
||||
onBeforeLoad(event:OnLoadEvent):void,
|
||||
onAfterLoad(event:OnLoadEvent):void,
|
||||
syncStarted: boolean;
|
||||
noteId: string;
|
||||
isProvisional: boolean;
|
||||
titleInputRef: any;
|
||||
editorRef: any;
|
||||
onBeforeLoad(event: OnLoadEvent): void;
|
||||
onAfterLoad(event: OnLoadEvent): void;
|
||||
}
|
||||
|
||||
function installResourceChangeHandler(onResourceChangeHandler: Function) {
|
||||
|
@ -41,7 +41,7 @@ function uninstallResourceChangeHandler(onResourceChangeHandler: Function) {
|
|||
ResourceEditWatcher.instance().off('resourceChange', onResourceChangeHandler);
|
||||
}
|
||||
|
||||
function resourceInfosChanged(a:ResourceInfos, b:ResourceInfos):boolean {
|
||||
function resourceInfosChanged(a: ResourceInfos, b: ResourceInfos): boolean {
|
||||
if (Object.keys(a).length !== Object.keys(b).length) return true;
|
||||
|
||||
for (const id in a) {
|
||||
|
@ -57,7 +57,7 @@ function resourceInfosChanged(a:ResourceInfos, b:ResourceInfos):boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
export default function useFormNote(dependencies:HookDependencies) {
|
||||
export default function useFormNote(dependencies: HookDependencies) {
|
||||
const { syncStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad } = dependencies;
|
||||
|
||||
const [formNote, setFormNote] = useState<FormNote>(defaultFormNote());
|
||||
|
@ -186,7 +186,7 @@ export default function useFormNote(dependencies:HookDependencies) {
|
|||
};
|
||||
}, [noteId, isProvisional, formNote]);
|
||||
|
||||
const onResourceChange = useCallback(async function(event:any = null) {
|
||||
const onResourceChange = useCallback(async function(event: any = null) {
|
||||
const resourceIds = await Note.linkedResourceIds(formNote.body);
|
||||
if (!event || resourceIds.indexOf(event.id) >= 0) {
|
||||
clearResourceCache();
|
||||
|
@ -213,7 +213,7 @@ export default function useFormNote(dependencies:HookDependencies) {
|
|||
async function runEffect() {
|
||||
const r = await attachedResources(formNote.body);
|
||||
if (cancelled) return;
|
||||
setResourceInfos((previous:ResourceInfos) => {
|
||||
setResourceInfos((previous: ResourceInfos) => {
|
||||
return resourceInfosChanged(previous, r) ? r : previous;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,17 +9,17 @@ const { themeStyle } = require('@joplin/lib/theme');
|
|||
const Note = require('@joplin/lib/models/Note');
|
||||
|
||||
interface HookDependencies {
|
||||
themeId: number,
|
||||
customCss: string,
|
||||
plugins: PluginStates,
|
||||
themeId: number;
|
||||
customCss: string;
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
interface MarkupToHtmlOptions {
|
||||
replaceResourceInternalToExternalLinks?: boolean,
|
||||
resourceInfos?: ResourceInfos,
|
||||
replaceResourceInternalToExternalLinks?: boolean;
|
||||
resourceInfos?: ResourceInfos;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps:HookDependencies) {
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
const { themeId, customCss, plugins } = deps;
|
||||
|
||||
const markupToHtml = useMemo(() => {
|
||||
|
|
|
@ -12,7 +12,7 @@ const urlUtils = require('@joplin/lib/urlUtils');
|
|||
const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
|
||||
export default function useMessageHandler(scrollWhenReady:any, setScrollWhenReady:Function, editorRef:any, setLocalSearchResultCount:Function, dispatch:Function, formNote:FormNote) {
|
||||
export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote) {
|
||||
return useCallback(async (event: any) => {
|
||||
const msg = event.channel ? event.channel : '';
|
||||
const args = event.args;
|
||||
|
|
|
@ -2,14 +2,14 @@ import { useState, useCallback } from 'react';
|
|||
import { SearchMarkers } from './useSearchMarkers';
|
||||
|
||||
interface LocalSearch {
|
||||
query: string,
|
||||
selectedIndex: number,
|
||||
resultCount: number,
|
||||
searching: boolean,
|
||||
timestamp: number,
|
||||
query: string;
|
||||
selectedIndex: number;
|
||||
resultCount: number;
|
||||
searching: boolean;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
function defaultLocalSearch():LocalSearch {
|
||||
function defaultLocalSearch(): LocalSearch {
|
||||
return {
|
||||
query: '',
|
||||
selectedIndex: 0,
|
||||
|
@ -23,8 +23,8 @@ export default function useNoteSearchBar() {
|
|||
const [showLocalSearch, setShowLocalSearch] = useState(false);
|
||||
const [localSearch, setLocalSearch] = useState<LocalSearch>(defaultLocalSearch());
|
||||
|
||||
const onChange = useCallback((query:string) => {
|
||||
setLocalSearch((prev:LocalSearch) => {
|
||||
const onChange = useCallback((query: string) => {
|
||||
setLocalSearch((prev: LocalSearch) => {
|
||||
return {
|
||||
query: query,
|
||||
selectedIndex: 0,
|
||||
|
@ -35,8 +35,8 @@ export default function useNoteSearchBar() {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const noteSearchBarNextPrevious = useCallback((inc:number) => {
|
||||
setLocalSearch((prev:LocalSearch) => {
|
||||
const noteSearchBarNextPrevious = useCallback((inc: number) => {
|
||||
setLocalSearch((prev: LocalSearch) => {
|
||||
const ls = Object.assign({}, prev);
|
||||
ls.selectedIndex += inc;
|
||||
ls.timestamp = Date.now();
|
||||
|
@ -59,8 +59,8 @@ export default function useNoteSearchBar() {
|
|||
setLocalSearch(defaultLocalSearch());
|
||||
}, []);
|
||||
|
||||
const setResultCount = useCallback((count:number) => {
|
||||
setLocalSearch((prev:LocalSearch) => {
|
||||
const setResultCount = useCallback((count: number) => {
|
||||
setLocalSearch((prev: LocalSearch) => {
|
||||
if (prev.resultCount === count && !prev.searching) return prev;
|
||||
|
||||
return {
|
||||
|
@ -71,7 +71,7 @@ export default function useNoteSearchBar() {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const searchMarkers = useCallback(():SearchMarkers => {
|
||||
const searchMarkers = useCallback((): SearchMarkers => {
|
||||
return {
|
||||
options: {
|
||||
selectedIndex: localSearch.selectedIndex,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from 'react';
|
||||
import PlatformImplementation from '../../../services/plugins/PlatformImplementation';
|
||||
|
||||
export default function usePluginServiceRegistration(ref:any) {
|
||||
export default function usePluginServiceRegistration(ref: any) {
|
||||
useEffect(() => {
|
||||
PlatformImplementation.instance().registerComponent('textEditor', ref);
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { useMemo } from 'react';
|
||||
|
||||
interface SearchMarkersOptions {
|
||||
searchTimestamp: number,
|
||||
selectedIndex: number,
|
||||
separateWordSearch: boolean,
|
||||
searchTimestamp: number;
|
||||
selectedIndex: number;
|
||||
separateWordSearch: boolean;
|
||||
}
|
||||
|
||||
export interface SearchMarkers {
|
||||
keywords: any[],
|
||||
options: SearchMarkersOptions,
|
||||
keywords: any[];
|
||||
options: SearchMarkersOptions;
|
||||
}
|
||||
|
||||
function defaultSearchMarkers():SearchMarkers {
|
||||
function defaultSearchMarkers(): SearchMarkers {
|
||||
return {
|
||||
keywords: [],
|
||||
options: {
|
||||
|
@ -23,8 +23,8 @@ function defaultSearchMarkers():SearchMarkers {
|
|||
}
|
||||
|
||||
|
||||
export default function useSearchMarkers(showLocalSearch:boolean, localSearchMarkerOptions:Function, searches:any[], selectedSearchId:string, highlightedWords: any[] = []) {
|
||||
return useMemo(():SearchMarkers => {
|
||||
export default function useSearchMarkers(showLocalSearch: boolean, localSearchMarkerOptions: Function, searches: any[], selectedSearchId: string, highlightedWords: any[] = []) {
|
||||
return useMemo((): SearchMarkers => {
|
||||
if (showLocalSearch) return localSearchMarkerOptions();
|
||||
|
||||
const output = defaultSearchMarkers();
|
||||
|
|
|
@ -12,18 +12,18 @@ const commandsWithDependencies = [
|
|||
];
|
||||
|
||||
interface HookDependencies {
|
||||
formNote:FormNote,
|
||||
setShowLocalSearch:Function,
|
||||
dispatch:Function,
|
||||
noteSearchBarRef:any,
|
||||
editorRef:any,
|
||||
titleInputRef:any,
|
||||
saveNoteAndWait: Function,
|
||||
formNote: FormNote;
|
||||
setShowLocalSearch: Function;
|
||||
dispatch: Function;
|
||||
noteSearchBarRef: any;
|
||||
editorRef: any;
|
||||
titleInputRef: any;
|
||||
saveNoteAndWait: Function;
|
||||
}
|
||||
|
||||
function editorCommandRuntime(declaration:CommandDeclaration, editorRef:any):CommandRuntime {
|
||||
function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any): CommandRuntime {
|
||||
return {
|
||||
execute: async (_context:CommandContext, ...args:any[]) => {
|
||||
execute: async (_context: CommandContext, ...args: any[]) => {
|
||||
if (!editorRef.current.execCommand) {
|
||||
reg.logger().warn('Received command, but editor cannot execute commands', declaration.name);
|
||||
return;
|
||||
|
@ -50,7 +50,7 @@ function editorCommandRuntime(declaration:CommandDeclaration, editorRef:any):Com
|
|||
};
|
||||
}
|
||||
|
||||
export default function useWindowCommandHandler(dependencies:HookDependencies) {
|
||||
export default function useWindowCommandHandler(dependencies: HookDependencies) {
|
||||
const { setShowLocalSearch, noteSearchBarRef, editorRef, titleInputRef } = dependencies;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -23,8 +23,8 @@ const commands = [
|
|||
const StyledRoot = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
border-right: 1px solid ${(props:any) => props.theme.dividerColor};
|
||||
background-color: ${(props: any) => props.theme.backgroundColor3};
|
||||
border-right: 1px solid ${(props: any) => props.theme.dividerColor};
|
||||
`;
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
|
@ -104,7 +104,7 @@ class NoteListComponent extends React.Component {
|
|||
return style;
|
||||
}
|
||||
|
||||
itemContextMenu(event:any) {
|
||||
itemContextMenu(event: any) {
|
||||
const currentItemId = event.currentTarget.getAttribute('data-id');
|
||||
if (!currentItemId) return;
|
||||
|
||||
|
@ -143,11 +143,11 @@ class NoteListComponent extends React.Component {
|
|||
document.removeEventListener('dragend', this.onGlobalDrop_);
|
||||
}
|
||||
|
||||
dragTargetNoteIndex_(event:any) {
|
||||
dragTargetNoteIndex_(event: any) {
|
||||
return Math.abs(Math.round((event.clientY - this.itemListRef.current.offsetTop() + this.itemListRef.current.offsetScroll()) / this.itemHeight));
|
||||
}
|
||||
|
||||
noteItem_noteDragOver(event:any) {
|
||||
noteItem_noteDragOver(event: any) {
|
||||
if (this.props.notesParentType !== 'Folder') return;
|
||||
|
||||
const dt = event.dataTransfer;
|
||||
|
@ -161,7 +161,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
async noteItem_noteDrop(event:any) {
|
||||
async noteItem_noteDrop(event: any) {
|
||||
if (this.props.notesParentType !== 'Folder') return;
|
||||
|
||||
if (this.props.noteSortOrder !== 'order') {
|
||||
|
@ -187,7 +187,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
|
||||
|
||||
async noteItem_checkboxClick(event:any, item:any) {
|
||||
async noteItem_checkboxClick(event: any, item: any) {
|
||||
const checked = event.target.checked;
|
||||
const newNote = {
|
||||
id: item.id,
|
||||
|
@ -197,7 +197,7 @@ class NoteListComponent extends React.Component {
|
|||
eventManager.emit('todoToggle', { noteId: item.id, note: newNote });
|
||||
}
|
||||
|
||||
async noteItem_titleClick(event:any, item:any) {
|
||||
async noteItem_titleClick(event: any, item: any) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
this.props.dispatch({
|
||||
|
@ -218,7 +218,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
noteItem_dragStart(event:any) {
|
||||
noteItem_dragStart(event: any) {
|
||||
let noteIds = [];
|
||||
|
||||
// Here there is two cases:
|
||||
|
@ -238,7 +238,7 @@ class NoteListComponent extends React.Component {
|
|||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||
}
|
||||
|
||||
renderItem(item:any, index:number) {
|
||||
renderItem(item: any, index: number) {
|
||||
const highlightedWords = () => {
|
||||
if (this.props.notesParentType === 'Search') {
|
||||
const query = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
|
@ -276,12 +276,12 @@ class NoteListComponent extends React.Component {
|
|||
/>;
|
||||
}
|
||||
|
||||
itemAnchorRef(itemId:string) {
|
||||
itemAnchorRef(itemId: string) {
|
||||
if (this.itemAnchorRefs_[itemId] && this.itemAnchorRefs_[itemId].current) return this.itemAnchorRefs_[itemId].current;
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps:any) {
|
||||
componentDidUpdate(prevProps: any) {
|
||||
if (prevProps.selectedNoteIds !== this.props.selectedNoteIds && this.props.selectedNoteIds.length === 1) {
|
||||
const id = this.props.selectedNoteIds[0];
|
||||
const doRefocus = this.props.notes.length < prevProps.notes.length;
|
||||
|
@ -303,7 +303,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
scrollNoteIndex_(keyCode:any, ctrlKey:any, metaKey:any, noteIndex:any) {
|
||||
scrollNoteIndex_(keyCode: any, ctrlKey: any, metaKey: any, noteIndex: any) {
|
||||
|
||||
if (keyCode === 33) {
|
||||
// Page Up
|
||||
|
@ -334,7 +334,7 @@ class NoteListComponent extends React.Component {
|
|||
return noteIndex;
|
||||
}
|
||||
|
||||
async onKeyDown(event:any) {
|
||||
async onKeyDown(event: any) {
|
||||
const keyCode = event.keyCode;
|
||||
const noteIds = this.props.selectedNoteIds;
|
||||
|
||||
|
@ -370,7 +370,7 @@ class NoteListComponent extends React.Component {
|
|||
event.preventDefault();
|
||||
|
||||
const notes = BaseModel.modelsByIds(this.props.notes, noteIds);
|
||||
const todos = notes.filter((n:any) => !!n.is_todo);
|
||||
const todos = notes.filter((n: any) => !!n.is_todo);
|
||||
if (!todos.length) return;
|
||||
|
||||
for (let i = 0; i < todos.length; i++) {
|
||||
|
@ -402,7 +402,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
focusNoteId_(noteId:string) {
|
||||
focusNoteId_(noteId: string) {
|
||||
// - We need to focus the item manually otherwise focus might be lost when the
|
||||
// list is scrolled and items within it are being rebuilt.
|
||||
// - We need to use an interval because when leaving the arrow pressed, the rendering
|
||||
|
@ -465,7 +465,7 @@ class NoteListComponent extends React.Component {
|
|||
return <div style={emptyDivStyle}>{this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
|
||||
}
|
||||
|
||||
renderItemList(style:any) {
|
||||
renderItemList(style: any) {
|
||||
if (!this.props.notes.length) return null;
|
||||
|
||||
return (
|
||||
|
@ -494,7 +494,7 @@ class NoteListComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:AppState) => {
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
notes: state.notes,
|
||||
folders: state.folders,
|
||||
|
|
|
@ -2,15 +2,15 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
|
|||
import { _ } from '@joplin/lib/locale';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementNoteList',
|
||||
label: () => _('Note list'),
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
if (noteId) {
|
||||
|
|
|
@ -7,15 +7,15 @@ import { runtime as focusSearchRuntime } from './commands/focusSearch';
|
|||
const styled = require('styled-components').default;
|
||||
|
||||
interface Props {
|
||||
showNewNoteButtons: boolean,
|
||||
showNewNoteButtons: boolean;
|
||||
}
|
||||
|
||||
const StyledRoot = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
padding: ${(props: any) => props.theme.mainPadding}px;
|
||||
background-color: ${(props: any) => props.theme.backgroundColor3};
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
|
@ -27,7 +27,7 @@ const ButtonContainer = styled.div`
|
|||
flex-direction: row;
|
||||
`;
|
||||
|
||||
export default function NoteListControls(props:Props) {
|
||||
export default function NoteListControls(props: Props) {
|
||||
const searchBarRef = useRef(null);
|
||||
|
||||
useEffect(function() {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusSearch',
|
||||
label: () => _('Search in all the notes'),
|
||||
};
|
||||
|
||||
export const runtime = (searchBarRef:any):CommandRuntime => {
|
||||
export const runtime = (searchBarRef: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
if (searchBarRef.current) searchBarRef.current.focus();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue