You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-02 00:08:04 +02:00
Compare commits
29 Commits
mobile_plu
...
api_pagina
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7bfd5dbe7 | ||
|
|
9a4b8685d2 | ||
|
|
f3376c779e | ||
|
|
76739eeff0 | ||
|
|
6d2bad01e6 | ||
|
|
3847831d80 | ||
|
|
44462f4d71 | ||
|
|
4d20589773 | ||
|
|
52140ec1a9 | ||
|
|
08c2a7ad64 | ||
|
|
d19796f14c | ||
|
|
e4f53a48d2 | ||
|
|
154163bd6c | ||
|
|
4502414934 | ||
|
|
86c471afcd | ||
|
|
279998db2b | ||
|
|
8cfe4b0f82 | ||
|
|
18608f4c3a | ||
|
|
5059089479 | ||
|
|
6284c386fb | ||
|
|
3a63df6515 | ||
|
|
c08d225ae1 | ||
|
|
594720530e | ||
|
|
e7d6675f47 | ||
|
|
0d93f0c3c0 | ||
|
|
37b308d440 | ||
|
|
1f8f3f24ed | ||
|
|
3713c5ab2c | ||
|
|
3a94d2c49b |
@@ -60,6 +60,7 @@ Tools/PortableAppsLauncher
|
||||
Modules/TinyMCE/IconPack/postinstall.js
|
||||
Modules/TinyMCE/langs/
|
||||
CliClient/build/
|
||||
plugin_types/
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
@@ -126,6 +127,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
@@ -148,8 +150,10 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
@@ -206,8 +210,10 @@ ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ElectronClient/services/spellChecker/SpellCheckerServiceDriverNative.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/BaseModel.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
@@ -251,6 +257,9 @@ ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/markupLanguageUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/models/utils/modelFeed.js
|
||||
ReactNativeClient/lib/models/utils/paginationToSql.js
|
||||
ReactNativeClient/lib/models/utils/types.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/path-utils.js
|
||||
ReactNativeClient/lib/PoorManIntervals.js
|
||||
@@ -325,10 +334,27 @@ ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/rest/ApiResponse.js
|
||||
ReactNativeClient/lib/services/rest/routes/folders.js
|
||||
ReactNativeClient/lib/services/rest/routes/master_keys.js
|
||||
ReactNativeClient/lib/services/rest/routes/notes.js
|
||||
ReactNativeClient/lib/services/rest/routes/ping.js
|
||||
ReactNativeClient/lib/services/rest/routes/resources.js
|
||||
ReactNativeClient/lib/services/rest/routes/search.js
|
||||
ReactNativeClient/lib/services/rest/routes/tags.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultAction.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultLoadOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultSaveOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/errors.js
|
||||
ReactNativeClient/lib/services/rest/utils/paginatedResults.js
|
||||
ReactNativeClient/lib/services/rest/utils/readonlyProperties.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestFields.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestPaginationOptions.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerService.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
@@ -351,6 +377,7 @@ ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/time.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
|
||||
28
.gitignore
vendored
28
.gitignore
vendored
@@ -120,6 +120,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
@@ -142,8 +143,10 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
@@ -200,8 +203,10 @@ ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ElectronClient/services/spellChecker/SpellCheckerServiceDriverNative.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/BaseModel.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
@@ -245,6 +250,9 @@ ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/markupLanguageUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/models/utils/modelFeed.js
|
||||
ReactNativeClient/lib/models/utils/paginationToSql.js
|
||||
ReactNativeClient/lib/models/utils/types.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/path-utils.js
|
||||
ReactNativeClient/lib/PoorManIntervals.js
|
||||
@@ -319,10 +327,27 @@ ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/rest/ApiResponse.js
|
||||
ReactNativeClient/lib/services/rest/routes/folders.js
|
||||
ReactNativeClient/lib/services/rest/routes/master_keys.js
|
||||
ReactNativeClient/lib/services/rest/routes/notes.js
|
||||
ReactNativeClient/lib/services/rest/routes/ping.js
|
||||
ReactNativeClient/lib/services/rest/routes/resources.js
|
||||
ReactNativeClient/lib/services/rest/routes/search.js
|
||||
ReactNativeClient/lib/services/rest/routes/tags.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultAction.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultLoadOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultSaveOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/errors.js
|
||||
ReactNativeClient/lib/services/rest/utils/paginatedResults.js
|
||||
ReactNativeClient/lib/services/rest/utils/readonlyProperties.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestFields.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestPaginationOptions.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerService.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
@@ -345,6 +370,7 @@ ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/time.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
|
||||
28
.ignore
28
.ignore
@@ -69,6 +69,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
@@ -91,8 +92,10 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
@@ -149,8 +152,10 @@ ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ElectronClient/services/spellChecker/SpellCheckerServiceDriverNative.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/BaseModel.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
@@ -194,6 +199,9 @@ ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/markupLanguageUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/models/utils/modelFeed.js
|
||||
ReactNativeClient/lib/models/utils/paginationToSql.js
|
||||
ReactNativeClient/lib/models/utils/types.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/path-utils.js
|
||||
ReactNativeClient/lib/PoorManIntervals.js
|
||||
@@ -268,10 +276,27 @@ ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/rest/ApiResponse.js
|
||||
ReactNativeClient/lib/services/rest/routes/folders.js
|
||||
ReactNativeClient/lib/services/rest/routes/master_keys.js
|
||||
ReactNativeClient/lib/services/rest/routes/notes.js
|
||||
ReactNativeClient/lib/services/rest/routes/ping.js
|
||||
ReactNativeClient/lib/services/rest/routes/resources.js
|
||||
ReactNativeClient/lib/services/rest/routes/search.js
|
||||
ReactNativeClient/lib/services/rest/routes/tags.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultAction.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultLoadOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/defaultSaveOptions.js
|
||||
ReactNativeClient/lib/services/rest/utils/errors.js
|
||||
ReactNativeClient/lib/services/rest/utils/paginatedResults.js
|
||||
ReactNativeClient/lib/services/rest/utils/readonlyProperties.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestFields.js
|
||||
ReactNativeClient/lib/services/rest/utils/requestPaginationOptions.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerService.js
|
||||
ReactNativeClient/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
@@ -294,6 +319,7 @@ ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/time.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 48 KiB |
@@ -2,7 +2,7 @@ const Logger = require('lib/Logger').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const BaseApplication = require('lib/BaseApplication').default;
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
@@ -5,7 +5,7 @@ const Logger = require('lib/Logger').default;
|
||||
const { dirname } = require('lib/path-utils');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const yargParser = require('yargs-parser');
|
||||
const { _ } = require('lib/locale');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const stringPadding = require('string-padding');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const { toTitleCase } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
const { Database } = require('lib/database.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
return 'apidoc';
|
||||
return 'apidoc <file>';
|
||||
}
|
||||
|
||||
description() {
|
||||
@@ -35,7 +36,7 @@ class Command extends BaseCommand {
|
||||
return markdownUtils.createMarkdownTable(headers, tableFields);
|
||||
}
|
||||
|
||||
async action() {
|
||||
async action(args) {
|
||||
const models = [
|
||||
{
|
||||
type: BaseModel.TYPE_NOTE,
|
||||
@@ -112,6 +113,51 @@ class Command extends BaseCommand {
|
||||
lines.push('');
|
||||
lines.push('\tcurl http://localhost:41184/tags?fields=id');
|
||||
lines.push('');
|
||||
lines.push('By default API results will contain the following fields: **id**, **parent_id**, **title**');
|
||||
|
||||
lines.push('# Pagination');
|
||||
lines.push('');
|
||||
lines.push('All API calls that return multiple results will be paginated. The actual results will be under the `items` key, and if there are more results, there will also be a `cursor` key, which allows you to fetch the next results. If the `cursor` key is not present, it means you have reached the end of the data set.');
|
||||
lines.push('');
|
||||
lines.push('You can specify how the results should be sorted using the `order_by` and `order_dir` query parameters, and you can specify the number of items to be returned using the `limit` parameter (the maximum being 100 items).');
|
||||
lines.push('');
|
||||
lines.push('The following call for example will initiate a request to fetch all the notes, 10 at a time, and sorted by "updated_time" ascending:');
|
||||
lines.push('');
|
||||
lines.push('\tcurl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10');
|
||||
lines.push('');
|
||||
lines.push('This will return a result like this');
|
||||
lines.push('');
|
||||
lines.push('\t{ "items": [ /* 10 notes */ ], "cursor": "somecursor" }');
|
||||
lines.push('');
|
||||
lines.push('Then you will resume fetching the results using this query:');
|
||||
lines.push('');
|
||||
lines.push('\tcurl http://localhost:41184/notes?cursor=somecursor');
|
||||
lines.push('');
|
||||
lines.push('Note that you only need to pass the cursor to the next request, as it will continue the fetching process using the same parameters you initially provided.');
|
||||
lines.push('');
|
||||
lines.push('Eventually you will get some results that do not contain a "cursor" paramater, at which point you will have retrieved all the results');
|
||||
lines.push('');
|
||||
lines.push('As an example the pseudo-code below could be used to fetch all the notes:');
|
||||
lines.push('');
|
||||
lines.push('```javascript');
|
||||
lines.push(`
|
||||
async function fetchJson(url) {
|
||||
return (await fetch(url)).json();
|
||||
}
|
||||
|
||||
async function fetchAllNotes() {
|
||||
let query = '';
|
||||
const url = 'http://localhost:41184/notes';
|
||||
|
||||
do {
|
||||
const response = await fetchJson(url + query);
|
||||
console.info('Printing notes:');
|
||||
console.info(response.items);
|
||||
query = '?cursor' + response.cursor;
|
||||
} while (response.cursor)
|
||||
}`);
|
||||
lines.push('```');
|
||||
lines.push('');
|
||||
|
||||
lines.push('# Error handling');
|
||||
lines.push('');
|
||||
@@ -314,7 +360,9 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
this.stdout(lines.join('\n'));
|
||||
const outFilePath = args['file'];
|
||||
|
||||
await shim.fsDriver().writeFile(outFilePath, lines.join('\n'), 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -45,10 +45,12 @@ class Command extends BaseCommand {
|
||||
|
||||
const startDecryption = async () => {
|
||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const result = await DecryptionWorker.instance().start();
|
||||
|
||||
if (result.error) throw result.error;
|
||||
|
||||
const line = [];
|
||||
line.push(_('Decrypted items: %d', result.decryptedItemCount));
|
||||
if (result.skippedItemCount) line.push(_('Skipped items: %d (use --retry-failed-items to retry decrypting them)', result.skippedItemCount));
|
||||
|
||||
@@ -6,7 +6,7 @@ const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -2,7 +2,7 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const { Database } = require('lib/database.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const time = require('lib/time').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { dirname } = require('lib/path-utils');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const _ = require('lib/locale')._;
|
||||
|
||||
|
||||
@@ -13,8 +13,10 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.3.1\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
|
||||
#: CliClient/app/command-cp.js:13
|
||||
msgid ""
|
||||
@@ -315,8 +317,9 @@ msgid "Sync to provided target (defaults to sync.target config value)"
|
||||
msgstr "Synchroniseren naar opgegeven doel (standaard is dit sync.target)"
|
||||
|
||||
#: CliClient/app/command-sync.js:35
|
||||
#, fuzzy
|
||||
msgid "Upgrade the sync target to the latest version."
|
||||
msgstr ""
|
||||
msgstr "Sync target updaten naar de nieuwste versie."
|
||||
|
||||
#: CliClient/app/command-sync.js:81 CliClient/app/command-sync.js:95
|
||||
#: ElectronClient/gui/OneDriveLoginScreen.min.js:40
|
||||
@@ -818,6 +821,8 @@ msgstr "Annuleren"
|
||||
msgid ""
|
||||
"The app is now going to close. Please relaunch it to complete the process."
|
||||
msgstr ""
|
||||
"De applicatie zal nu afsluiten. Gelieve het weer op te starten om het proces "
|
||||
"te voltooien."
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.min.js:459
|
||||
msgid ""
|
||||
@@ -835,15 +840,14 @@ msgid "Goto Anything..."
|
||||
msgstr "Ga naar Alles..."
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.js:431
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Type a note title or part of its content to jump to it. Or type # followed "
|
||||
"by a tag name, or @ followed by a notebook name. Or type : to search for "
|
||||
"commands."
|
||||
msgstr ""
|
||||
"Typ de titel van een notitie of een deel van de inhoud om er naartoe te "
|
||||
"springen. Of typ # gevolgd door de naam van een label, of @ gevolgd door de "
|
||||
"naam van een notitieboek."
|
||||
"springen. Typ # gevolgd door de naam van een label, @ gevolgd door de naam "
|
||||
"van een notitieboek, of : om te zoeken op commando's."
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.js:463
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28
|
||||
@@ -884,9 +888,8 @@ msgid "New version: %s"
|
||||
msgstr "Nieuwe versie: %s"
|
||||
|
||||
#: ElectronClient/checkForUpdates.js:154
|
||||
#, fuzzy
|
||||
msgid "Download"
|
||||
msgstr "Gedownload"
|
||||
msgstr "Downloaden"
|
||||
|
||||
#: ElectronClient/checkForUpdates.js:154
|
||||
msgid "Full Release Notes"
|
||||
@@ -1235,13 +1238,15 @@ msgstr "Bezig met creëren van nieuw(e) %s..."
|
||||
|
||||
#: ElectronClient/gui/NoteEditor/NoteEditor.js:379
|
||||
msgid "The following attachments are being watched for changes:"
|
||||
msgstr ""
|
||||
msgstr "De volgende bijlagen worden gecontroleerd op wijzigingen:"
|
||||
|
||||
#: ElectronClient/gui/NoteEditor/NoteEditor.js:382
|
||||
msgid ""
|
||||
"The attachments will no longer be watched when you switch to a different "
|
||||
"note."
|
||||
msgstr ""
|
||||
"De bijlagen worden niet meer gecontroleerd wanneer je naar een andere "
|
||||
"notitie schakelt."
|
||||
|
||||
#: ElectronClient/gui/NoteEditor/NoteEditor.js:387
|
||||
#: ElectronClient/gui/NoteText.min.js:1656
|
||||
@@ -1358,9 +1363,9 @@ msgstr "Eigenschappen van notitie"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:63
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:144
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Error: %s"
|
||||
msgstr "Fout"
|
||||
msgstr "Fout: %s"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:121
|
||||
#: ElectronClient/gui/Root.min.js:91 ElectronClient/gui/MenuBar.js:391
|
||||
@@ -1370,12 +1375,11 @@ msgstr "Importeren"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:126
|
||||
msgid "Command"
|
||||
msgstr ""
|
||||
msgstr "Commando"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:127
|
||||
#, fuzzy
|
||||
msgid "Keyboard Shortcut"
|
||||
msgstr "Toetsenbordmodus"
|
||||
msgstr "Sneltoets"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:14
|
||||
#: ElectronClient/gui/MenuBar.js:178 ElectronClient/app.js:347
|
||||
@@ -1398,9 +1402,8 @@ msgid "Website and documentation"
|
||||
msgstr "Website en documentatie"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:24
|
||||
#, fuzzy
|
||||
msgid "Hide Joplin"
|
||||
msgstr "Over Joplin"
|
||||
msgstr "Joplin verbergen"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:26
|
||||
#: ElectronClient/gui/MenuBar.js:428
|
||||
@@ -1408,9 +1411,8 @@ msgid "Close Window"
|
||||
msgstr "Venster afsluiten"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30
|
||||
#, fuzzy
|
||||
msgid "Preferences"
|
||||
msgstr "Voorkeuren..."
|
||||
msgstr "Voorkeuren"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:30
|
||||
#: ElectronClient/gui/Root.min.js:92 ElectronClient/gui/MenuBar.js:304
|
||||
@@ -1420,13 +1422,15 @@ msgstr "Opties"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
|
||||
msgid "Press the shortcut"
|
||||
msgstr ""
|
||||
msgstr "Druk op de sneltoetsen"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
|
||||
msgid ""
|
||||
"Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the "
|
||||
"shortcut."
|
||||
msgstr ""
|
||||
"Druk op de sneltoetsen en dan op ENTER. Of, druk op backspace om de "
|
||||
"sneltoets te wissen."
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:50
|
||||
#: ElectronClient/gui/EncryptionConfigScreen.min.js:96
|
||||
@@ -1436,17 +1440,20 @@ msgstr "Opslaan"
|
||||
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.js:418
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.min.js:301
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The sync target needs to be upgraded before Joplin can sync. The operation "
|
||||
"may take a few minutes to complete and the app needs to be restarted. To "
|
||||
"proceed please click on the link."
|
||||
msgstr ""
|
||||
"De sync target moet worden geüpdatet voordat Joplin kan synchroniseren. Het "
|
||||
"kan een paar minuten duren en de app moet opnieuw opgestart worden. Om door "
|
||||
"te gaan, klik op de link."
|
||||
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.js:420
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.min.js:306
|
||||
#, fuzzy
|
||||
msgid "Restart and upgrade"
|
||||
msgstr "Hoofdsleutels die moeten worden bijgewerkt"
|
||||
msgstr "Opnieuw opstarten en bijwerken"
|
||||
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.js:424
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.min.js:313
|
||||
@@ -2268,18 +2275,16 @@ msgstr[0] "Kopieer Deelbare Link"
|
||||
msgstr[1] "Kopieer Deelbare Links"
|
||||
|
||||
#: ElectronClient/commands/toggleExternalEditing.js:18
|
||||
#, fuzzy
|
||||
msgid "Toggle external editing"
|
||||
msgstr "Klik om extern bewerken te stoppen"
|
||||
msgstr "Extern bewerken in- of uitschakelen"
|
||||
|
||||
#: ElectronClient/commands/toggleExternalEditing.js:37
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
msgstr "Stop"
|
||||
|
||||
#: ElectronClient/commands/copyDevCommand.js:18
|
||||
#, fuzzy
|
||||
msgid "Copy dev mode command to clipboard"
|
||||
msgstr "Pad kopiëren naar klembord"
|
||||
msgstr "Ontwikkelaarsmodus commando kopiëren naar klembord"
|
||||
|
||||
#: ElectronClient/commands/stopExternalEditing.js:18
|
||||
msgid "Stop external editing"
|
||||
@@ -2291,9 +2296,8 @@ msgid "Error opening note in editor: %s"
|
||||
msgstr "Fout bij openen van notitie in editor: %s"
|
||||
|
||||
#: ElectronClient/commands/openProfileDirectory.js:18
|
||||
#, fuzzy
|
||||
msgid "Open profile directory"
|
||||
msgstr "Open sjabloon map"
|
||||
msgstr "Open profiel map"
|
||||
|
||||
#: ElectronClient/app.js:345
|
||||
#, javascript-format
|
||||
@@ -2339,7 +2343,7 @@ msgstr ""
|
||||
|
||||
#: ReactNativeClient/lib/SyncTargetAmazonS3.js:28
|
||||
msgid "AWS S3"
|
||||
msgstr ""
|
||||
msgstr "AWS S3"
|
||||
|
||||
#: ReactNativeClient/lib/SyncTargetDropbox.js:25
|
||||
msgid "Dropbox"
|
||||
@@ -2483,19 +2487,19 @@ msgstr "WebDAV-wachtwoord"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:215
|
||||
msgid "AWS S3 bucket"
|
||||
msgstr ""
|
||||
msgstr "AWS S3 bucket"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:226
|
||||
msgid "AWS S3 URL"
|
||||
msgstr ""
|
||||
msgstr "AWS S3 URL"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:237
|
||||
msgid "AWS key"
|
||||
msgstr ""
|
||||
msgstr "AWS key"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:247
|
||||
msgid "AWS secret"
|
||||
msgstr ""
|
||||
msgstr "AWS secret"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:259
|
||||
msgid "Attachment download behaviour"
|
||||
@@ -2955,9 +2959,8 @@ msgid "Web Clipper"
|
||||
msgstr "Webclipper"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:1310
|
||||
#, fuzzy
|
||||
msgid "Keyboard Shortcuts"
|
||||
msgstr "Toetsenbordmodus"
|
||||
msgstr "Sneltoetsen"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:1317
|
||||
msgid ""
|
||||
@@ -3074,9 +3077,8 @@ msgid "Save alarm"
|
||||
msgstr "Alarm opslaan"
|
||||
|
||||
#: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
|
||||
#, fuzzy
|
||||
msgid "Open"
|
||||
msgstr "Openen..."
|
||||
msgstr "Openen"
|
||||
|
||||
#: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:29
|
||||
#: ReactNativeClient/lib/components/screens/Note.js:794
|
||||
@@ -3114,8 +3116,9 @@ msgstr ""
|
||||
"Sommige items kunnen niet worden gesynchroniseerd. Klik voor meer informatie."
|
||||
|
||||
#: ReactNativeClient/lib/components/screen-header.js:453
|
||||
#, fuzzy
|
||||
msgid "The sync target needs to be upgraded. Press this banner to proceed."
|
||||
msgstr ""
|
||||
msgstr "De sync target moet worden geüpdatet. Druk hier om door te gaan."
|
||||
|
||||
#: ReactNativeClient/lib/components/side-menu-content.js:126
|
||||
#, javascript-format
|
||||
@@ -3239,8 +3242,9 @@ msgid "Refresh"
|
||||
msgstr "Verversen"
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js:42
|
||||
#, fuzzy
|
||||
msgid "Sync Target Upgrade"
|
||||
msgstr ""
|
||||
msgstr "Sync target update"
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/NoteTagsDialog.js:163
|
||||
msgid "New tags:"
|
||||
@@ -3484,11 +3488,14 @@ msgid ""
|
||||
"\n"
|
||||
"You may turn off this option at any time in the Configuration screen."
|
||||
msgstr ""
|
||||
"Om een geolocatie aan de notitie te koppelen, heeft de app uw toestemming "
|
||||
"nodig om toegang te krijgen tot uw locatie.\n"
|
||||
"\n"
|
||||
"U kunt deze optie op elk moment uitschakelen in het 'Configuratie'-scherm."
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/Note.js:341
|
||||
#, fuzzy
|
||||
msgid "Permission needed"
|
||||
msgstr "Toestemming om de camera te gebruiken"
|
||||
msgstr "Toestemming vereist"
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/Note.js:583
|
||||
#, javascript-format
|
||||
@@ -3737,23 +3744,23 @@ msgstr "Geef een importformaat op voor %s"
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:240
|
||||
msgid "command"
|
||||
msgstr ""
|
||||
msgstr "commando"
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:240
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:245
|
||||
#, javascript-format
|
||||
msgid "\"%s\" is missing the required \"%s\" property."
|
||||
msgstr ""
|
||||
msgstr "\"%s\" mist de vereiste \"%s\" eigenschap."
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:245
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:252
|
||||
msgid "accelerator"
|
||||
msgstr ""
|
||||
msgstr "versneller"
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:252
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Invalid %s: %s."
|
||||
msgstr "Ongeldig antwoord: %s"
|
||||
msgstr "Ongeldig %s: %s."
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:270
|
||||
#, javascript-format
|
||||
@@ -3761,11 +3768,13 @@ msgid ""
|
||||
"Accelerator \"%s\" is used for \"%s\" and \"%s\" commands. This may lead to "
|
||||
"unexpected behaviour."
|
||||
msgstr ""
|
||||
"Versneller \"%s\" wordt gebruikt voor \"%s\" en \"%s\" commando's. Dit kan "
|
||||
"tot onverwacht gedrag leiden."
|
||||
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:295
|
||||
#, javascript-format
|
||||
msgid "Accelerator \"%s\" is not valid."
|
||||
msgstr ""
|
||||
msgstr "Versneller \"%s\" is niet geldig."
|
||||
|
||||
#: ReactNativeClient/lib/services/report.js:121
|
||||
msgid "Items that cannot be synchronised"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const os = require('os');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { filename } = require('lib/path-utils');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const os = require('os');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { filename } = require('lib/path-utils');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const HtmlToHtml = require('lib/joplin-renderer/HtmlToHtml');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const os = require('os');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { filename } = require('lib/path-utils');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const HtmlToMd = require('lib/HtmlToMd');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import InMemoryCache from 'lib/InMemoryCache';
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
describe('InMemoryCache', function() {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import MdToHtml from 'lib/joplin-renderer/MdToHtml';
|
||||
const os = require('os');
|
||||
const { filename } = require('lib/path-utils');
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const MdToHtml = require('lib/joplin-renderer/MdToHtml').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
function newTestMdToHtml(options:any = null) {
|
||||
@@ -120,23 +120,54 @@ describe('MdToHtml', function() {
|
||||
it('should return the rendered body only', asyncTest(async () => {
|
||||
const mdToHtml = newTestMdToHtml();
|
||||
|
||||
// In this case, the HTML contains only the rendered markdown,
|
||||
// with no wrapper and no style.
|
||||
// The style is instead in the cssStrings property.
|
||||
const result = await mdToHtml.render('just **testing**', null, { bodyOnly: true });
|
||||
expect(result.cssStrings.length).toBeGreaterThan(0);
|
||||
expect(result.html.trim()).toBe('just <strong>testing</strong>');
|
||||
// In this case, the HTML contains only the rendered markdown, with
|
||||
// no wrapper and no style. The style is instead in the cssStrings
|
||||
// property.
|
||||
{
|
||||
const result = await mdToHtml.render('just **testing**', null, { bodyOnly: true });
|
||||
expect(result.cssStrings.length).toBeGreaterThan(0);
|
||||
expect(result.html.trim()).toBe('just <strong>testing</strong>');
|
||||
}
|
||||
|
||||
// But it should not remove the wrapping <p> tags if there's more
|
||||
// than one line
|
||||
{
|
||||
const result = await mdToHtml.render('one\n\ntwo', null, { bodyOnly: true });
|
||||
expect(result.html.trim()).toBe('<p>one</p>\n<p>two</p>');
|
||||
}
|
||||
}));
|
||||
|
||||
it('should split HTML and CSS', asyncTest(async () => {
|
||||
const mdToHtml = newTestMdToHtml();
|
||||
|
||||
// It is similar to the bodyOnly option, excepts that
|
||||
// the rendered Markdown is wrapped in a DIV
|
||||
// It is similar to the bodyOnly option, excepts that the rendered
|
||||
// Markdown is wrapped in a DIV
|
||||
const result = await mdToHtml.render('just **testing**', null, { splitted: true });
|
||||
expect(result.cssStrings.length).toBeGreaterThan(0);
|
||||
expect(result.html.trim()).toBe('<div id="rendered-md"><p>just <strong>testing</strong></p>\n</div>');
|
||||
}));
|
||||
|
||||
it('should render links correctly', asyncTest(async () => {
|
||||
const mdToHtml = newTestMdToHtml();
|
||||
|
||||
const testCases = [
|
||||
// None of these should result in a link
|
||||
['https://example.com', 'https://example.com'],
|
||||
['file://C:\\AUTOEXEC.BAT', 'file://C:\\AUTOEXEC.BAT'],
|
||||
['example.com', 'example.com'],
|
||||
['oo.ps', 'oo.ps'],
|
||||
['test@example.com', 'test@example.com'],
|
||||
|
||||
// Those should be converted to links
|
||||
['<https://example.com>', '<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>https://example.com</a>'],
|
||||
['[ok](https://example.com)', '<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>ok</a>'],
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const [input, expected] = testCase;
|
||||
const actual = await mdToHtml.render(input, null, { bodyOnly: true, plainResourceRendering: true });
|
||||
expect(actual.html).toBe(expected);
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { asyncTest, id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('test-utils.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const uuid = require('lib/uuid').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
@@ -5,7 +5,7 @@ const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
let testApp = null;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids.js');
|
||||
|
||||
//
|
||||
|
||||
@@ -5,7 +5,7 @@ const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
let testApp = null;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, sleep, fileApi, fileContentEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const fs = require('fs-extra');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, revisionService, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
@@ -10,7 +10,7 @@ const Tag = require('lib/models/Tag.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService.js');
|
||||
|
||||
@@ -5,7 +5,7 @@ require('app-module-path').addPath(__dirname);
|
||||
const fs = require('fs-extra');
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const InteropService_Exporter_Md = require('lib/services/interop/InteropService_Exporter_Md').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
@@ -3,6 +3,7 @@ require('app-module-path').addPath(__dirname);
|
||||
const { tempFilePath } = require('test-utils.js');
|
||||
const KeymapService = require('lib/services/KeymapService').default;
|
||||
const keymapService = KeymapService.instance();
|
||||
keymapService.initialize([]);
|
||||
|
||||
describe('services_KeymapService', () => {
|
||||
describe('validateAccelerator', () => {
|
||||
@@ -31,7 +32,7 @@ describe('services_KeymapService', () => {
|
||||
};
|
||||
|
||||
Object.entries(testCases).forEach(([platform, accelerators]) => {
|
||||
keymapService.initialize(platform);
|
||||
keymapService.initialize([], platform);
|
||||
accelerators.forEach(accelerator => {
|
||||
expect(() => keymapService.validateAccelerator(accelerator)).not.toThrow();
|
||||
});
|
||||
@@ -69,7 +70,7 @@ describe('services_KeymapService', () => {
|
||||
};
|
||||
|
||||
Object.entries(testCases).forEach(([platform, accelerators]) => {
|
||||
keymapService.initialize(platform);
|
||||
keymapService.initialize([], platform);
|
||||
accelerators.forEach(accelerator => {
|
||||
expect(() => keymapService.validateAccelerator(accelerator)).toThrow();
|
||||
});
|
||||
@@ -81,12 +82,12 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should allow registering new commands', async () => {
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize([], 'linux');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B');
|
||||
|
||||
// Check that macOS key conversion is working
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B');
|
||||
keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X');
|
||||
@@ -95,7 +96,7 @@ describe('services_KeymapService', () => {
|
||||
const keymapFilePath = tempFilePath('json');
|
||||
await keymapService.saveCustomKeymap(keymapFilePath);
|
||||
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
await keymapService.loadCustomKeymap(keymapFilePath);
|
||||
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X');
|
||||
@@ -106,17 +107,17 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should return the platform-specific default Accelerator', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
expect(keymapService.getAccelerator('newNote')).toEqual('Cmd+N');
|
||||
expect(keymapService.getAccelerator('synchronize')).toEqual('Cmd+S');
|
||||
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Cmd+A');
|
||||
expect(keymapService.getAccelerator('textBold')).toEqual('Cmd+B');
|
||||
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize([], 'linux');
|
||||
expect(keymapService.getAccelerator('newNote')).toEqual('Ctrl+N');
|
||||
expect(keymapService.getAccelerator('synchronize')).toEqual('Ctrl+S');
|
||||
|
||||
keymapService.initialize('win32');
|
||||
keymapService.initialize([], 'win32');
|
||||
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Ctrl+A');
|
||||
expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B');
|
||||
});
|
||||
@@ -130,7 +131,7 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should update the Accelerator', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize(['print'], 'darwin');
|
||||
const testCases_Darwin = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Option+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'F11' },
|
||||
@@ -147,7 +148,7 @@ describe('services_KeymapService', () => {
|
||||
expect(keymapService.getAccelerator(command)).toEqual(accelerator);
|
||||
});
|
||||
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize(['print'], 'linux');
|
||||
const testCases_Linux = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'F15' },
|
||||
@@ -167,7 +168,7 @@ describe('services_KeymapService', () => {
|
||||
});
|
||||
|
||||
describe('getDefaultAccelerator', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
beforeEach(() => keymapService.initialize(['print', 'linux']));
|
||||
|
||||
it('should return the default accelerator', () => {
|
||||
const testCases = [
|
||||
@@ -196,7 +197,7 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should update the keymap', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
const customKeymapItems_Darwin = [
|
||||
{ command: 'newNote', accelerator: 'Option+Shift+Cmd+N' },
|
||||
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
|
||||
@@ -217,7 +218,7 @@ describe('services_KeymapService', () => {
|
||||
expect(keymapService.getAccelerator(command)).toEqual(accelerator);
|
||||
});
|
||||
|
||||
keymapService.initialize('win32');
|
||||
keymapService.initialize([], 'win32');
|
||||
const customKeymapItems_Win32 = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
@@ -11,7 +11,7 @@ const NoteTag = require('lib/models/NoteTag.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const RevisionService = require('lib/services/RevisionService.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, mockDate, restoreDate } = require('test-utils.js');
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const Note = require('lib/models/Note');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('test-utils.js');
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const Note = require('lib/models/Note');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// require('app-module-path').addPath(__dirname);
|
||||
|
||||
// const { time } = require('lib/time-utils.js');
|
||||
// const time = require('lib/time').default;
|
||||
// const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('test-utils.js');
|
||||
// const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
// const Note = require('lib/models/Note');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Api from 'lib/services/rest/Api';
|
||||
import { PaginationOrderDir } from 'lib/models/utils/types';
|
||||
import Api, { RequestMethod } from 'lib/services/rest/Api';
|
||||
import shim from 'lib/shim';
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
|
||||
@@ -8,9 +9,21 @@ const Note = require('lib/models/Note');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const NoteTag = require('lib/models/NoteTag');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
async function msleep(ms:number) {
|
||||
return new Promise((resolve) => {
|
||||
shim.setTimeout(() => {
|
||||
resolve();
|
||||
}, ms);
|
||||
});
|
||||
}
|
||||
|
||||
const createFolderForPagination = async (num:number, time:number) => {
|
||||
await Folder.save({
|
||||
title: `folder${num}`,
|
||||
updated_time: time,
|
||||
created_time: time,
|
||||
}, { autoTimestamp: false });
|
||||
};
|
||||
|
||||
let api:Api = null;
|
||||
|
||||
@@ -24,24 +37,24 @@ describe('services_rest_Api', function() {
|
||||
});
|
||||
|
||||
it('should ping', asyncTest(async () => {
|
||||
const response = await api.route('GET', 'ping');
|
||||
const response = await api.route(RequestMethod.GET, 'ping');
|
||||
expect(response).toBe('JoplinClipperServer');
|
||||
}));
|
||||
|
||||
it('should handle Not Found errors', asyncTest(async () => {
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'pong'));
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'pong'));
|
||||
expect(hasThrown).toBe(true);
|
||||
}));
|
||||
|
||||
it('should get folders', asyncTest(async () => {
|
||||
await Folder.save({ title: 'mon carnet' });
|
||||
const response = await api.route('GET', 'folders');
|
||||
expect(response.length).toBe(1);
|
||||
const response = await api.route(RequestMethod.GET, 'folders');
|
||||
expect(response.items.length).toBe(1);
|
||||
}));
|
||||
|
||||
it('should update folders', asyncTest(async () => {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
await api.route('PUT', `folders/${f1.id}`, null, JSON.stringify({
|
||||
await api.route(RequestMethod.PUT, `folders/${f1.id}`, null, JSON.stringify({
|
||||
title: 'modifié',
|
||||
}));
|
||||
|
||||
@@ -51,14 +64,14 @@ describe('services_rest_Api', function() {
|
||||
|
||||
it('should delete folders', asyncTest(async () => {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
await api.route('DELETE', `folders/${f1.id}`);
|
||||
await api.route(RequestMethod.DELETE, `folders/${f1.id}`);
|
||||
|
||||
const f1b = await Folder.load(f1.id);
|
||||
expect(!f1b).toBe(true);
|
||||
}));
|
||||
|
||||
it('should create folders', asyncTest(async () => {
|
||||
const response = await api.route('POST', 'folders', null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.POST, 'folders', null, JSON.stringify({
|
||||
title: 'from api',
|
||||
}));
|
||||
|
||||
@@ -71,26 +84,26 @@ describe('services_rest_Api', function() {
|
||||
|
||||
it('should get one folder', asyncTest(async () => {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
const response = await api.route('GET', `folders/${f1.id}`);
|
||||
const response = await api.route(RequestMethod.GET, `folders/${f1.id}`);
|
||||
expect(response.id).toBe(f1.id);
|
||||
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'folders/doesntexist'));
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'folders/doesntexist'));
|
||||
expect(hasThrown).toBe(true);
|
||||
}));
|
||||
|
||||
it('should get the folder notes', asyncTest(async () => {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
const response2 = await api.route('GET', `folders/${f1.id}/notes`);
|
||||
expect(response2.length).toBe(0);
|
||||
const response2 = await api.route(RequestMethod.GET, `folders/${f1.id}/notes`);
|
||||
expect(response2.items.length).toBe(0);
|
||||
|
||||
await Note.save({ title: 'un', parent_id: f1.id });
|
||||
await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const response = await api.route('GET', `folders/${f1.id}/notes`);
|
||||
expect(response.length).toBe(2);
|
||||
const response = await api.route(RequestMethod.GET, `folders/${f1.id}/notes`);
|
||||
expect(response.items.length).toBe(2);
|
||||
}));
|
||||
|
||||
it('should fail on invalid paths', asyncTest(async () => {
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'schtroumpf'));
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'schtroumpf'));
|
||||
expect(hasThrown).toBe(true);
|
||||
}));
|
||||
|
||||
@@ -102,13 +115,13 @@ describe('services_rest_Api', function() {
|
||||
await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const n3 = await Note.save({ title: 'trois', parent_id: f2.id });
|
||||
|
||||
response = await api.route('GET', 'notes');
|
||||
expect(response.length).toBe(3);
|
||||
response = await api.route(RequestMethod.GET, 'notes');
|
||||
expect(response.items.length).toBe(3);
|
||||
|
||||
response = await api.route('GET', `notes/${n1.id}`);
|
||||
response = await api.route(RequestMethod.GET, `notes/${n1.id}`);
|
||||
expect(response.id).toBe(n1.id);
|
||||
|
||||
response = await api.route('GET', `notes/${n3.id}`, { fields: 'id,title' });
|
||||
response = await api.route(RequestMethod.GET, `notes/${n3.id}`, { fields: 'id,title' });
|
||||
expect(Object.getOwnPropertyNames(response).length).toBe(3);
|
||||
expect(response.id).toBe(n3.id);
|
||||
expect(response.title).toBe('trois');
|
||||
@@ -118,14 +131,14 @@ describe('services_rest_Api', function() {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
}));
|
||||
expect(response.title).toBe('testing');
|
||||
expect(!!response.id).toBe(true);
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
}));
|
||||
@@ -137,7 +150,7 @@ describe('services_rest_Api', function() {
|
||||
let response:any = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
latitude: '48.732071',
|
||||
@@ -154,7 +167,7 @@ describe('services_rest_Api', function() {
|
||||
expect(note.altitude).toBe('21.0000');
|
||||
}
|
||||
|
||||
await api.route('PUT', `notes/${noteId}`, null, JSON.stringify({
|
||||
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
||||
latitude: '49',
|
||||
longitude: '-3',
|
||||
altitude: '22',
|
||||
@@ -175,7 +188,7 @@ describe('services_rest_Api', function() {
|
||||
const updatedTime = Date.now() - 1000;
|
||||
const createdTime = Date.now() - 10000;
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
parent_id: f.id,
|
||||
user_updated_time: updatedTime,
|
||||
user_created_time: createdTime,
|
||||
@@ -186,7 +199,7 @@ describe('services_rest_Api', function() {
|
||||
|
||||
const timeBefore = Date.now();
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
parent_id: f.id,
|
||||
}));
|
||||
|
||||
@@ -201,7 +214,7 @@ describe('services_rest_Api', function() {
|
||||
const updatedTime = Date.now() - 1000;
|
||||
const createdTime = Date.now() - 10000;
|
||||
|
||||
const response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
parent_id: folder.id,
|
||||
}));
|
||||
|
||||
@@ -210,7 +223,7 @@ describe('services_rest_Api', function() {
|
||||
{
|
||||
// Check that if user timestamps are supplied, they are preserved by the API
|
||||
|
||||
await api.route('PUT', `notes/${noteId}`, null, JSON.stringify({
|
||||
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
||||
user_updated_time: updatedTime,
|
||||
user_created_time: createdTime,
|
||||
title: 'mod',
|
||||
@@ -227,7 +240,7 @@ describe('services_rest_Api', function() {
|
||||
|
||||
const beforeTime = Date.now();
|
||||
|
||||
await api.route('PUT', `notes/${noteId}`, null, JSON.stringify({
|
||||
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
||||
title: 'mod2',
|
||||
}));
|
||||
|
||||
@@ -242,7 +255,7 @@ describe('services_rest_Api', function() {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
id: '12345678123456781234567812345678',
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
@@ -254,27 +267,27 @@ describe('services_rest_Api', function() {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'stuff to do' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
is_todo: 1,
|
||||
}));
|
||||
expect(response.is_todo).toBe(1);
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing 2',
|
||||
parent_id: f.id,
|
||||
is_todo: 0,
|
||||
}));
|
||||
expect(response.is_todo).toBe(0);
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing 3',
|
||||
parent_id: f.id,
|
||||
}));
|
||||
expect(response.is_todo).toBeUndefined();
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing 4',
|
||||
parent_id: f.id,
|
||||
is_todo: '1',
|
||||
@@ -282,7 +295,7 @@ describe('services_rest_Api', function() {
|
||||
}));
|
||||
|
||||
it('should create folders with supplied ID', asyncTest(async () => {
|
||||
const response = await api.route('POST', 'folders', null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.POST, 'folders', null, JSON.stringify({
|
||||
id: '12345678123456781234567812345678',
|
||||
title: 'from api',
|
||||
}));
|
||||
@@ -294,7 +307,7 @@ describe('services_rest_Api', function() {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing image',
|
||||
parent_id: f.id,
|
||||
image_data_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=',
|
||||
@@ -310,7 +323,7 @@ describe('services_rest_Api', function() {
|
||||
it('should delete resources', asyncTest(async () => {
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
await api.route('POST', 'notes', null, JSON.stringify({
|
||||
await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing image',
|
||||
parent_id: f.id,
|
||||
image_data_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=',
|
||||
@@ -321,7 +334,7 @@ describe('services_rest_Api', function() {
|
||||
const filePath = Resource.fullPath(resource);
|
||||
expect(await shim.fsDriver().exists(filePath)).toBe(true);
|
||||
|
||||
await api.route('DELETE', `resources/${resource.id}`);
|
||||
await api.route(RequestMethod.DELETE, `resources/${resource.id}`);
|
||||
expect(await shim.fsDriver().exists(filePath)).toBe(false);
|
||||
expect(!(await Resource.load(resource.id))).toBe(true);
|
||||
}));
|
||||
@@ -330,7 +343,7 @@ describe('services_rest_Api', function() {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
||||
title: 'testing HTML',
|
||||
parent_id: f.id,
|
||||
body_html: '<b>Bold text</b>',
|
||||
@@ -339,32 +352,32 @@ describe('services_rest_Api', function() {
|
||||
expect(response.body).toBe('**Bold text**');
|
||||
}));
|
||||
|
||||
it('should filter fields', asyncTest(async () => {
|
||||
let f = api.fields_({ query: { fields: 'one,two' } }, []);
|
||||
expect(f.length).toBe(2);
|
||||
expect(f[0]).toBe('one');
|
||||
expect(f[1]).toBe('two');
|
||||
// it('should filter fields', asyncTest(async () => {
|
||||
// let f = api.fields_({ query: { fields: 'one,two' } } as any, []);
|
||||
// expect(f.length).toBe(2);
|
||||
// expect(f[0]).toBe('one');
|
||||
// expect(f[1]).toBe('two');
|
||||
|
||||
f = api.fields_({ query: { fields: 'one ,, two ' } }, []);
|
||||
expect(f.length).toBe(2);
|
||||
expect(f[0]).toBe('one');
|
||||
expect(f[1]).toBe('two');
|
||||
// f = api.fields_({ query: { fields: 'one ,, two ' } } as any, []);
|
||||
// expect(f.length).toBe(2);
|
||||
// expect(f[0]).toBe('one');
|
||||
// expect(f[1]).toBe('two');
|
||||
|
||||
f = api.fields_({ query: { fields: ' ' } }, ['def']);
|
||||
expect(f.length).toBe(1);
|
||||
expect(f[0]).toBe('def');
|
||||
}));
|
||||
// f = api.fields_({ query: { fields: ' ' } } as any, ['def']);
|
||||
// expect(f.length).toBe(1);
|
||||
// expect(f[0]).toBe('def');
|
||||
// }));
|
||||
|
||||
it('should handle tokens', asyncTest(async () => {
|
||||
api = new Api('mytoken');
|
||||
|
||||
let hasThrown = await checkThrowAsync(async () => await api.route('GET', 'notes'));
|
||||
let hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'notes'));
|
||||
expect(hasThrown).toBe(true);
|
||||
|
||||
const response = await api.route('GET', 'notes', { token: 'mytoken' });
|
||||
expect(response.length).toBe(0);
|
||||
const response = await api.route(RequestMethod.GET, 'notes', { token: 'mytoken' });
|
||||
expect(response.items.length).toBe(0);
|
||||
|
||||
hasThrown = await checkThrowAsync(async () => await api.route('POST', 'notes', null, JSON.stringify({ title: 'testing' })));
|
||||
hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({ title: 'testing' })));
|
||||
expect(hasThrown).toBe(true);
|
||||
}));
|
||||
|
||||
@@ -372,7 +385,7 @@ describe('services_rest_Api', function() {
|
||||
const tag = await Tag.save({ title: 'mon étiquette' });
|
||||
const note = await Note.save({ title: 'ma note' });
|
||||
|
||||
await api.route('POST', `tags/${tag.id}/notes`, null, JSON.stringify({
|
||||
await api.route(RequestMethod.POST, `tags/${tag.id}/notes`, null, JSON.stringify({
|
||||
id: note.id,
|
||||
}));
|
||||
|
||||
@@ -385,7 +398,7 @@ describe('services_rest_Api', function() {
|
||||
const note = await Note.save({ title: 'ma note' });
|
||||
await Tag.addNote(tag.id, note.id);
|
||||
|
||||
await api.route('DELETE', `tags/${tag.id}/notes/${note.id}`);
|
||||
await api.route(RequestMethod.DELETE, `tags/${tag.id}/notes/${note.id}`);
|
||||
|
||||
const noteIds = await Tag.noteIds(tag.id);
|
||||
expect(noteIds.length).toBe(0);
|
||||
@@ -399,16 +412,16 @@ describe('services_rest_Api', function() {
|
||||
await Tag.addNote(tag.id, note1.id);
|
||||
await Tag.addNote(tag.id, note2.id);
|
||||
|
||||
const response = await api.route('GET', `tags/${tag.id}/notes`);
|
||||
expect(response.length).toBe(2);
|
||||
expect('id' in response[0]).toBe(true);
|
||||
expect('title' in response[0]).toBe(true);
|
||||
const response = await api.route(RequestMethod.GET, `tags/${tag.id}/notes`);
|
||||
expect(response.items.length).toBe(2);
|
||||
expect('id' in response.items[0]).toBe(true);
|
||||
expect('title' in response.items[0]).toBe(true);
|
||||
|
||||
const response2 = await api.route('GET', `notes/${note1.id}/tags`);
|
||||
expect(response2.length).toBe(1);
|
||||
const response2 = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
||||
expect(response2.items.length).toBe(1);
|
||||
await Tag.addNote(tag2.id, note1.id);
|
||||
const response3 = await api.route('GET', `notes/${note1.id}/tags`);
|
||||
expect(response3.length).toBe(2);
|
||||
const response3 = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
||||
expect(response3.items.length).toBe(2);
|
||||
}));
|
||||
|
||||
it('should update tags when updating notes', asyncTest(async () => {
|
||||
@@ -422,7 +435,7 @@ describe('services_rest_Api', function() {
|
||||
Tag.addNote(tag1.id, note.id);
|
||||
Tag.addNote(tag2.id, note.id);
|
||||
|
||||
const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
||||
tags: `${tag1.title},${tag3.title}`,
|
||||
}));
|
||||
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
||||
@@ -443,7 +456,7 @@ describe('services_rest_Api', function() {
|
||||
Tag.addNote(tag1.id, note.id);
|
||||
Tag.addNote(tag2.id, note.id);
|
||||
|
||||
const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
||||
tags: `${tag1.title},${newTagTitle}`,
|
||||
}));
|
||||
const newTag = await Tag.loadByTitle(newTagTitle);
|
||||
@@ -464,7 +477,7 @@ describe('services_rest_Api', function() {
|
||||
Tag.addNote(tag1.id, note.id);
|
||||
Tag.addNote(tag2.id, note.id);
|
||||
|
||||
const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
||||
title: 'Some other title',
|
||||
}));
|
||||
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
||||
@@ -484,11 +497,160 @@ describe('services_rest_Api', function() {
|
||||
Tag.addNote(tag1.id, note.id);
|
||||
Tag.addNote(tag2.id, note.id);
|
||||
|
||||
const response = await api.route('PUT', `notes/${note.id}`, null, JSON.stringify({
|
||||
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
||||
tags: '',
|
||||
}));
|
||||
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
||||
expect(response.tags === '').toBe(true);
|
||||
expect(tagIds.length === 0).toBe(true);
|
||||
}));
|
||||
|
||||
it('should paginate results', asyncTest(async () => {
|
||||
await createFolderForPagination(1, 1001);
|
||||
await createFolderForPagination(2, 1002);
|
||||
await createFolderForPagination(3, 1003);
|
||||
await createFolderForPagination(4, 1004);
|
||||
|
||||
{
|
||||
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
||||
fields: ['id', 'title', 'updated_time'],
|
||||
limit: 2,
|
||||
order_dir: PaginationOrderDir.ASC,
|
||||
order_by: 'updated_time',
|
||||
});
|
||||
|
||||
expect(r1.items.length).toBe(2);
|
||||
expect(r1.items[0].title).toBe('folder1');
|
||||
expect(r1.items[1].title).toBe('folder2');
|
||||
|
||||
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
||||
cursor: r1.cursor,
|
||||
});
|
||||
|
||||
expect(r2.items.length).toBe(2);
|
||||
expect(r2.items[0].title).toBe('folder3');
|
||||
expect(r2.items[1].title).toBe('folder4');
|
||||
|
||||
const r3 = await api.route(RequestMethod.GET, 'folders', {
|
||||
cursor: r2.cursor,
|
||||
});
|
||||
|
||||
expect(r3.items.length).toBe(0);
|
||||
expect(r3.cursor).toBe(undefined);
|
||||
}
|
||||
|
||||
{
|
||||
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
||||
fields: ['id', 'title', 'updated_time'],
|
||||
limit: 3,
|
||||
order_dir: PaginationOrderDir.ASC,
|
||||
order_by: 'updated_time',
|
||||
});
|
||||
|
||||
expect(r1.items.length).toBe(3);
|
||||
expect(r1.items[0].title).toBe('folder1');
|
||||
expect(r1.items[1].title).toBe('folder2');
|
||||
expect(r1.items[2].title).toBe('folder3');
|
||||
|
||||
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
||||
cursor: r1.cursor,
|
||||
});
|
||||
|
||||
expect(r2.items.length).toBe(1);
|
||||
expect(r2.items[0].title).toBe('folder4');
|
||||
expect(r2.cursor).toBe(undefined);
|
||||
}
|
||||
}));
|
||||
|
||||
it('should paginate results and handle duplicate cursor field value', asyncTest(async () => {
|
||||
await createFolderForPagination(1, 1001);
|
||||
await createFolderForPagination(2, 1002);
|
||||
await createFolderForPagination(3, 1002);
|
||||
await createFolderForPagination(4, 1003);
|
||||
|
||||
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
||||
fields: ['id', 'title', 'updated_time'],
|
||||
limit: 2,
|
||||
order_dir: PaginationOrderDir.ASC,
|
||||
order_by: 'updated_time',
|
||||
});
|
||||
|
||||
expect(r1.items.length).toBe(2);
|
||||
expect(r1.items[0].title).toBe('folder1');
|
||||
expect(['folder2', 'folder3'].includes(r1.items[1].title)).toBe(true);
|
||||
|
||||
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
||||
cursor: r1.cursor,
|
||||
});
|
||||
|
||||
expect(r2.items.length).toBe(2);
|
||||
expect(r2.items[0].title).toBe(r1.items[1].title === 'folder2' ? 'folder3' : 'folder2');
|
||||
expect(r2.items[1].title).toBe('folder4');
|
||||
}));
|
||||
|
||||
it('should paginate folder notes', asyncTest(async () => {
|
||||
const folder = await Folder.save({});
|
||||
const note1 = await Note.save({ parent_id: folder.id });
|
||||
await msleep(1);
|
||||
const note2 = await Note.save({ parent_id: folder.id });
|
||||
await msleep(1);
|
||||
const note3 = await Note.save({ parent_id: folder.id });
|
||||
|
||||
const r1 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
|
||||
limit: 2,
|
||||
});
|
||||
|
||||
expect(r1.items.length).toBe(2);
|
||||
expect(r1.items[0].id).toBe(note1.id);
|
||||
expect(r1.items[1].id).toBe(note2.id);
|
||||
|
||||
const r2 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
|
||||
cursor: r1.cursor,
|
||||
});
|
||||
|
||||
expect(r2.items.length).toBe(1);
|
||||
expect(r2.items[0].id).toBe(note3.id);
|
||||
}));
|
||||
|
||||
it('should return default fields', asyncTest(async () => {
|
||||
const folder = await Folder.save({ title: 'folder' });
|
||||
const note1 = await Note.save({ title: 'note1', parent_id: folder.id });
|
||||
await Note.save({ title: 'note2', parent_id: folder.id });
|
||||
|
||||
const tag = await Tag.save({ title: 'tag' });
|
||||
await Tag.addNote(tag.id, note1.id);
|
||||
|
||||
{
|
||||
const r = await api.route(RequestMethod.GET, `folders/${folder.id}`);
|
||||
expect('id' in r).toBe(true);
|
||||
expect('title' in r).toBe(true);
|
||||
expect('parent_id' in r).toBe(true);
|
||||
}
|
||||
|
||||
{
|
||||
const r = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`);
|
||||
expect('id' in r.items[0]).toBe(true);
|
||||
expect('title' in r.items[0]).toBe(true);
|
||||
expect('parent_id' in r.items[0]).toBe(true);
|
||||
}
|
||||
|
||||
{
|
||||
const r = await api.route(RequestMethod.GET, 'notes');
|
||||
expect('id' in r.items[0]).toBe(true);
|
||||
expect('title' in r.items[0]).toBe(true);
|
||||
expect('parent_id' in r.items[0]).toBe(true);
|
||||
}
|
||||
|
||||
{
|
||||
const r = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
||||
expect('id' in r.items[0]).toBe(true);
|
||||
expect('title' in r.items[0]).toBe(true);
|
||||
}
|
||||
|
||||
{
|
||||
const r = await api.route(RequestMethod.GET, `tags/${tag.id}`);
|
||||
expect('id' in r).toBe(true);
|
||||
expect('title' in r).toBe(true);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { setupDatabase, synchronizerStart, syncTargetName, allSyncTargetItemsEncrypted, tempFilePath, resourceFetcher, kvStore, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, checkThrowAsync, asyncTest } = require('test-utils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const fs = require('fs-extra');
|
||||
@@ -16,7 +16,7 @@ const Setting = require('lib/models/Setting').default;
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
const WelcomeUtils = require('lib/WelcomeUtils');
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const fs = require('fs-extra');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const BaseApplication = require('lib/BaseApplication').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
@@ -25,7 +25,7 @@ const { FileApiDriverOneDrive } = require('lib/file-api-driver-onedrive.js');
|
||||
const { FileApiDriverAmazonS3 } = require('lib/file-api-driver-amazon-s3.js');
|
||||
const BaseService = require('lib/services/BaseService').default;
|
||||
const FsDriverNode = require('lib/fs-driver-node').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { shimInit } = require('lib/shim-init-node.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const uuid = require('lib/uuid').default;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const timeUtils = require('../../ReactNativeClient/lib/time-utils');
|
||||
const timeUtils = require('../../ReactNativeClient/lib/time');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
||||
@@ -86,6 +86,7 @@ export default class ElectronAppWrapper {
|
||||
backgroundColor: '#fff', // required to enable sub pixel rendering, can't be in css
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
spellcheck: true,
|
||||
},
|
||||
webviewTag: true,
|
||||
// We start with a hidden window, which is then made visible depending on the showTrayIcon setting
|
||||
@@ -97,14 +98,6 @@ export default class ElectronAppWrapper {
|
||||
// Fix: https://github.com/electron-userland/electron-builder/issues/2269
|
||||
if (shim.isLinux()) windowOptions.icon = path.join(__dirname, '..', 'build/icons/128x128.png');
|
||||
|
||||
require('electron-context-menu')({
|
||||
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';
|
||||
},
|
||||
});
|
||||
|
||||
this.win_ = new BrowserWindow(windowOptions);
|
||||
|
||||
if (!screen.getDisplayMatching(this.win_.getBounds())) {
|
||||
|
||||
@@ -8,7 +8,7 @@ const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { friendlySafeFilename } = require('lib/path-utils');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const md5 = require('md5');
|
||||
const url = require('url');
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import Setting from 'lib/models/Setting';
|
||||
import actionApi from 'lib/services/rest/actionApi.desktop';
|
||||
import BaseApplication from 'lib/BaseApplication';
|
||||
import { _, setLocale } from 'lib/locale';
|
||||
import SpellCheckerService from 'lib/services/spellChecker/SpellCheckerService';
|
||||
import SpellCheckerServiceDriverNative from './services/spellChecker/SpellCheckerServiceDriverNative';
|
||||
import bridge from './services/bridge';
|
||||
import menuCommandNames from './gui/menuCommandNames';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
@@ -28,7 +32,6 @@ const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const ClipperServer = require('lib/ClipperServer');
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { webFrame } = require('electron');
|
||||
const Menu = bridge().Menu;
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
@@ -451,6 +454,31 @@ class Application extends BaseApplication {
|
||||
document.head.appendChild(styleTag);
|
||||
}
|
||||
|
||||
setupContextMenu() {
|
||||
// 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) => {
|
||||
// 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);
|
||||
|
||||
const output = [
|
||||
actions.cut(),
|
||||
actions.copy(),
|
||||
actions.paste(),
|
||||
...spellCheckerMenuItems,
|
||||
];
|
||||
|
||||
return output;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async loadCustomCss(filePath:string) {
|
||||
let cssString = '';
|
||||
if (await fs.pathExists(filePath)) {
|
||||
@@ -495,14 +523,6 @@ class Application extends BaseApplication {
|
||||
const filename = Setting.custom_css_files.JOPLIN_APP;
|
||||
await CssUtils.injectCustomStyles(`${dir}/${filename}`);
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
try {
|
||||
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
|
||||
} catch (err) {
|
||||
reg.logger().error(err.message);
|
||||
}
|
||||
|
||||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||
AlarmService.setLogger(reg.logger());
|
||||
|
||||
@@ -533,9 +553,21 @@ class Application extends BaseApplication {
|
||||
CommandService.instance().registerDeclaration(declaration);
|
||||
}
|
||||
|
||||
// Since the settings need to be loaded before the store is created, it will never
|
||||
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
|
||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||
const keymapService = KeymapService.instance();
|
||||
// We only add the commands that appear in the menu because only
|
||||
// those can have a shortcut associated with them.
|
||||
keymapService.initialize(menuCommandNames());
|
||||
|
||||
try {
|
||||
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
|
||||
} catch (error) {
|
||||
reg.logger().error(error);
|
||||
}
|
||||
|
||||
// Since the settings need to be loaded before the store is
|
||||
// created, it will never receive the SETTING_UPDATE_ALL even,
|
||||
// which mean state.settings will not be initialised. So we
|
||||
// manually call dispatchUpdateAll() to force an update.
|
||||
Setting.dispatchUpdateAll();
|
||||
|
||||
await FoldersScreenUtils.refreshFolders();
|
||||
@@ -684,6 +716,10 @@ class Application extends BaseApplication {
|
||||
this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error);
|
||||
}
|
||||
|
||||
this.setupContextMenu();
|
||||
|
||||
await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative());
|
||||
|
||||
// await populateDatabase(reg.db());
|
||||
|
||||
// setTimeout(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ const Setting = require('lib/models/Setting').default;
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const dialogs = require('./dialogs');
|
||||
const shared = require('lib/components/shared/encryption-config-shared.js');
|
||||
|
||||
@@ -5,7 +5,7 @@ import { _ } from 'lib/locale';
|
||||
|
||||
const commandService = CommandService.instance();
|
||||
|
||||
const getLabel = (commandName: 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
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import KeymapService, { KeymapItem } from 'lib/services/KeymapService';
|
||||
import getLabel from './getLabel';
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
// This custom hook provides a synchronized snapshot of the keymap residing at KeymapService
|
||||
// All the logic regarding altering and interacting with the keymap is isolated from the components
|
||||
|
||||
function allKeymapItems() {
|
||||
const output = keymapService.getKeymapItems().slice();
|
||||
|
||||
output.sort((a:KeymapItem, b:KeymapItem) => {
|
||||
return getLabel(a.command).toLocaleLowerCase() < getLabel(b.command).toLocaleLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const useKeymap = (): [
|
||||
KeymapItem[],
|
||||
Error,
|
||||
@@ -13,7 +24,7 @@ const useKeymap = (): [
|
||||
(commandName: string, accelerator: string) => void,
|
||||
(commandName: string) => void
|
||||
] => {
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => keymapService.getKeymapItems());
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => allKeymapItems());
|
||||
const [keymapError, setKeymapError] = useState<Error>(null);
|
||||
const [mustSave, setMustSave] = useState(false);
|
||||
|
||||
@@ -42,7 +53,7 @@ const useKeymap = (): [
|
||||
|
||||
const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => {
|
||||
const oldKeymapItems = [...customKeymapItems];
|
||||
keymapService.initialize(); // Start with a fresh keymap
|
||||
keymapService.resetKeymap(); // Start with a fresh keymap
|
||||
|
||||
try {
|
||||
// First, try to update the in-memory keymap of KeymapService
|
||||
|
||||
@@ -27,7 +27,7 @@ const bridge = require('electron').remote.require('./bridge').default;
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
const StyledUserWebviewDialogContainer = styled.div`
|
||||
|
||||
@@ -3,7 +3,7 @@ import eventManager from 'lib/eventManager';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
const Note = require('lib/models/Note');
|
||||
const { time } = require('lib/time-utils');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'editAlarm',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const uuid = require('lib/uuid').default;
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
|
||||
@@ -14,6 +14,8 @@ import InteropServiceHelper from '../InteropServiceHelper';
|
||||
import { _ } from 'lib/locale';
|
||||
import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
import SpellCheckerService from 'lib/services/spellChecker/SpellCheckerService';
|
||||
import menuCommandNames from './menuCommandNames';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
@@ -84,41 +86,11 @@ interface Props {
|
||||
showCompletedTodos: boolean,
|
||||
pluginMenuItems: any[],
|
||||
pluginMenus: any[],
|
||||
['spellChecker.enabled']: boolean,
|
||||
['spellChecker.language']: string,
|
||||
}
|
||||
|
||||
const commandNames:string[] = [
|
||||
'focusElementSideBar',
|
||||
'focusElementNoteList',
|
||||
'focusElementNoteTitle',
|
||||
'focusElementNoteBody',
|
||||
'exportPdf',
|
||||
'newNote',
|
||||
'newTodo',
|
||||
'newFolder',
|
||||
'newSubFolder',
|
||||
'print',
|
||||
'synchronize',
|
||||
'textCopy',
|
||||
'textCut',
|
||||
'textPaste',
|
||||
'textSelectAll',
|
||||
'textBold',
|
||||
'textItalic',
|
||||
'textLink',
|
||||
'textCode',
|
||||
'insertDateTime',
|
||||
'attachFile',
|
||||
'focusSearch',
|
||||
'showLocalSearch',
|
||||
'toggleSideBar',
|
||||
'toggleNoteList',
|
||||
'toggleVisiblePanes',
|
||||
'toggleExternalEditing',
|
||||
'setTags',
|
||||
'showNoteContentProperties',
|
||||
'copyDevCommand',
|
||||
'openProfileDirectory',
|
||||
];
|
||||
const commandNames:string[] = menuCommandNames();
|
||||
|
||||
function menuItemSetChecked(id:string, checked:boolean) {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
@@ -249,10 +221,8 @@ function useMenu(props:Props) {
|
||||
menuItemDic.focusElementNoteBody,
|
||||
];
|
||||
|
||||
let toolsItems:any[] = [];
|
||||
const importItems = [];
|
||||
const exportItems = [];
|
||||
const toolsItemsFirst = [];
|
||||
const templateItems:any[] = [];
|
||||
const ioService = InteropService.instance();
|
||||
const ioModules = ioService.modules();
|
||||
@@ -299,16 +269,18 @@ function useMenu(props:Props) {
|
||||
},
|
||||
};
|
||||
|
||||
const separator = () => {
|
||||
return {
|
||||
type: 'separator',
|
||||
};
|
||||
};
|
||||
|
||||
const newNoteItem = menuItemDic.newNote;
|
||||
const newTodoItem = menuItemDic.newTodo;
|
||||
const newFolderItem = menuItemDic.newFolder;
|
||||
const newSubFolderItem = menuItemDic.newSubFolder;
|
||||
const printItem = menuItemDic.print;
|
||||
|
||||
toolsItemsFirst.push(syncStatusItem, {
|
||||
type: 'separator',
|
||||
});
|
||||
|
||||
templateItems.push({
|
||||
label: _('Create note from template'),
|
||||
click: () => {
|
||||
@@ -342,18 +314,22 @@ function useMenu(props:Props) {
|
||||
},
|
||||
});
|
||||
|
||||
let toolsItems:any[] = [];
|
||||
|
||||
// we need this workaround, because on macOS the menu is different
|
||||
const toolsItemsWindowsLinux:any[] = toolsItemsFirst.concat([{
|
||||
label: _('Options'),
|
||||
visible: !shim.isMac(),
|
||||
accelerator: !shim.isMac() && keymapService.getAccelerator('config'),
|
||||
click: () => {
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
});
|
||||
const toolsItemsWindowsLinux:any[] = [
|
||||
{
|
||||
label: _('Options'),
|
||||
accelerator: keymapService.getAccelerator('config'),
|
||||
click: () => {
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
});
|
||||
},
|
||||
},
|
||||
} as any]);
|
||||
separator(),
|
||||
];
|
||||
|
||||
// the following menu items will be available for all OS under Tools
|
||||
const toolsItemsAll = [{
|
||||
@@ -371,6 +347,8 @@ function useMenu(props:Props) {
|
||||
}
|
||||
toolsItems = toolsItems.concat(toolsItemsAll);
|
||||
|
||||
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.language'], props['spellChecker.enabled']));
|
||||
|
||||
function _checkForUpdates() {
|
||||
bridge().checkForUpdates(false, bridge().window(), `${Setting.value('profileDir')}/log-autoupdater.txt`, { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
}
|
||||
@@ -451,9 +429,7 @@ function useMenu(props:Props) {
|
||||
|
||||
menuItemDic.synchronize,
|
||||
|
||||
shim.isMac() ? syncStatusItem : noItem, {
|
||||
type: 'separator',
|
||||
}, shim.isMac() ? noItem : printItem, {
|
||||
shim.isMac() ? noItem : printItem, {
|
||||
type: 'separator',
|
||||
platforms: ['darwin'],
|
||||
},
|
||||
@@ -518,12 +494,6 @@ function useMenu(props:Props) {
|
||||
});
|
||||
}
|
||||
|
||||
const separator = () => {
|
||||
return {
|
||||
type: 'separator',
|
||||
};
|
||||
};
|
||||
|
||||
const rootMenus:any = {
|
||||
edit: {
|
||||
id: 'edit',
|
||||
@@ -586,11 +556,6 @@ function useMenu(props:Props) {
|
||||
},
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
label: _('Focus'),
|
||||
submenu: focusItems,
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
label: _('Actual Size'),
|
||||
click: () => {
|
||||
@@ -623,6 +588,18 @@ function useMenu(props:Props) {
|
||||
accelerator: 'CommandOrControl+-',
|
||||
}],
|
||||
},
|
||||
go: {
|
||||
label: _('&Go'),
|
||||
submenu: [
|
||||
menuItemDic.historyBackward,
|
||||
menuItemDic.historyForward,
|
||||
separator(),
|
||||
{
|
||||
label: _('Focus'),
|
||||
submenu: focusItems,
|
||||
},
|
||||
],
|
||||
},
|
||||
note: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
@@ -655,6 +632,8 @@ function useMenu(props:Props) {
|
||||
click: () => _checkForUpdates(),
|
||||
},
|
||||
separator(),
|
||||
syncStatusItem,
|
||||
separator(),
|
||||
{
|
||||
id: 'help:toggleDevTools',
|
||||
label: _('Toggle development tools'),
|
||||
@@ -709,6 +688,7 @@ function useMenu(props:Props) {
|
||||
const pluginMenuItems = PluginManager.instance().menuItems();
|
||||
for (const item of pluginMenuItems) {
|
||||
const itemParent = rootMenus[item.parent] ? rootMenus[item.parent] : 'tools';
|
||||
itemParent.submenu.push(separator());
|
||||
itemParent.submenu.push(item);
|
||||
}
|
||||
}
|
||||
@@ -741,6 +721,7 @@ function useMenu(props:Props) {
|
||||
rootMenus.file,
|
||||
rootMenus.edit,
|
||||
rootMenus.view,
|
||||
rootMenus.go,
|
||||
rootMenus.note,
|
||||
rootMenus.tools,
|
||||
rootMenus.help,
|
||||
@@ -748,46 +729,6 @@ function useMenu(props:Props) {
|
||||
|
||||
if (shim.isMac()) template.splice(0, 0, rootMenus.macOsApp);
|
||||
|
||||
// TODO
|
||||
|
||||
// function isEmptyMenu(template:any[]) {
|
||||
// for (let i = 0; i < template.length; i++) {
|
||||
// const t = template[i];
|
||||
// if (t.type !== 'separator') return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// function removeUnwantedItems(template:any[], screen:string) {
|
||||
// const platform = shim.platformName();
|
||||
|
||||
// let output = [];
|
||||
// for (let i = 0; i < template.length; i++) {
|
||||
// const t = Object.assign({}, template[i]);
|
||||
// if (t.screens && t.screens.indexOf(screen) < 0) continue;
|
||||
// if (t.platforms && t.platforms.indexOf(platform) < 0) continue;
|
||||
// if (t.submenu) t.submenu = removeUnwantedItems(t.submenu, screen);
|
||||
// if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
|
||||
// output.push(t);
|
||||
// }
|
||||
|
||||
// // Remove empty separator for now empty sections
|
||||
// const temp = [];
|
||||
// let previous = null;
|
||||
// for (let i = 0; i < output.length; i++) {
|
||||
// const t = Object.assign({}, output[i]);
|
||||
// if (t.type === 'separator') {
|
||||
// if (!previous) continue;
|
||||
// if (previous.type === 'separator') continue;
|
||||
// }
|
||||
// temp.push(t);
|
||||
// previous = t;
|
||||
// }
|
||||
// output = temp;
|
||||
|
||||
// return output;
|
||||
// }
|
||||
|
||||
if (props.routeName !== 'Main') {
|
||||
setMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
@@ -807,7 +748,7 @@ function useMenu(props:Props) {
|
||||
} else {
|
||||
setMenu(Menu.buildFromTemplate(template));
|
||||
}
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime]);
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled']]);
|
||||
|
||||
useEffect(() => {
|
||||
const whenClauseContext = CommandService.instance().currentWhenClauseContext();
|
||||
@@ -902,6 +843,8 @@ const mapStateToProps = (state:AppState) => {
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
pluginMenuItems: stateUtils.selectArrayShallow({ array: pluginUtils.viewsByType(state.pluginService.plugins, 'menuItem') }, 'menuBar.pluginMenuItems'),
|
||||
pluginMenus: stateUtils.selectArrayShallow({ array: pluginUtils.viewsByType(state.pluginService.plugins, 'menu') }, 'menuBar.pluginMenus'),
|
||||
['spellChecker.language']: state.settings['spellChecker.language'],
|
||||
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps }
|
||||
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||
import useScroll from './utils/useScroll';
|
||||
import styles_ from './styles';
|
||||
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../utils/contextMenu';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import { ToolbarButtonInfo } from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import ToggleEditorsButton, { Value as ToggleEditorsButtonValue } from '../../../ToggleEditorsButton/ToggleEditorsButton';
|
||||
@@ -12,13 +11,13 @@ import ToolbarButton from '../../../../gui/ToolbarButton/ToolbarButton';
|
||||
import usePluginServiceRegistration from '../../utils/usePluginServiceRegistration';
|
||||
import { utils as pluginUtils } from 'lib/services/plugins/reducer';
|
||||
import { _, closestSupportedLocale } from 'lib/locale';
|
||||
import setupContextMenu from './utils/setupContextMenu';
|
||||
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
const taboverride = require('taboverride');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const shim = require('lib/shim').default;
|
||||
const Resource = require('lib/models/Resource');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
@@ -143,8 +142,6 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
const props_onDrop = useRef(null);
|
||||
props_onDrop.current = props.onDrop;
|
||||
|
||||
const contextMenuActionOptions = useRef<ContextMenuOptions>(null);
|
||||
|
||||
const markupToHtml = useRef(null);
|
||||
markupToHtml.current = props.markupToHtml;
|
||||
|
||||
@@ -510,19 +507,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
loadedCssFiles_ = [];
|
||||
loadedJsFiles_ = [];
|
||||
|
||||
function contextMenuItemNameWithNamespace(name:string) {
|
||||
// For unknown reasons, TinyMCE converts all context menu names to
|
||||
// lowercase when setting them in the init method, so we need to
|
||||
// make them lowercase too, to make sure that the update() method
|
||||
// addContextMenu is triggered.
|
||||
return (`joplin${name}`).toLowerCase();
|
||||
}
|
||||
|
||||
const loadEditor = async () => {
|
||||
const contextMenuItems = menuItems();
|
||||
const contextMenuItemNames = [];
|
||||
for (const name in contextMenuItems) contextMenuItemNames.push(contextMenuItemNameWithNamespace(name));
|
||||
|
||||
const language = closestSupportedLocale(props.locale, true, supportedLocales);
|
||||
|
||||
const pluginCommandNames:string[] = [];
|
||||
@@ -564,7 +549,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
language: ['en_US', 'en_GB'].includes(language) ? undefined : language,
|
||||
toolbar: toolbar.join(' '),
|
||||
localization_function: _,
|
||||
contextmenu: contextMenuItemNames.join(' '),
|
||||
contextmenu: false,
|
||||
browser_spellcheck: true,
|
||||
setup: (editor:any) => {
|
||||
|
||||
function openEditDialog(editable:any) {
|
||||
@@ -680,51 +666,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const itemName in contextMenuItems) {
|
||||
const item = contextMenuItems[itemName];
|
||||
|
||||
const itemNameNS = contextMenuItemNameWithNamespace(itemName);
|
||||
|
||||
editor.ui.registry.addMenuItem(itemNameNS, {
|
||||
text: item.label,
|
||||
onAction: () => {
|
||||
item.onAction(contextMenuActionOptions.current);
|
||||
},
|
||||
});
|
||||
|
||||
editor.ui.registry.addContextMenu(itemNameNS, {
|
||||
update: function(element:any) {
|
||||
let itemType:ContextMenuItemType = ContextMenuItemType.None;
|
||||
let resourceId = '';
|
||||
let linkToCopy = null;
|
||||
|
||||
if (element.nodeName === 'IMG') {
|
||||
itemType = ContextMenuItemType.Image;
|
||||
resourceId = Resource.pathToId(element.src);
|
||||
} else if (element.nodeName === 'A') {
|
||||
resourceId = Resource.pathToId(element.href);
|
||||
itemType = resourceId ? ContextMenuItemType.Resource : ContextMenuItemType.Link;
|
||||
linkToCopy = element.getAttribute('href') || '';
|
||||
} else {
|
||||
itemType = ContextMenuItemType.Text;
|
||||
}
|
||||
|
||||
contextMenuActionOptions.current = {
|
||||
itemType,
|
||||
resourceId,
|
||||
linkToCopy,
|
||||
textToCopy: null,
|
||||
htmlToCopy: editor.selection ? editor.selection.getContent() : '',
|
||||
insertContent: (content:string) => {
|
||||
editor.insertContent(content);
|
||||
},
|
||||
isReadOnly: false,
|
||||
};
|
||||
|
||||
return item.isActive(itemType, contextMenuActionOptions.current) ? itemNameNS : '';
|
||||
},
|
||||
});
|
||||
}
|
||||
setupContextMenu(editor);
|
||||
|
||||
// TODO: remove event on unmount?
|
||||
editor.on('DblClick', (event:any) => {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import SpellCheckerService from 'lib/services/spellChecker/SpellCheckerService';
|
||||
import bridge from '../../../../../services/bridge';
|
||||
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../../utils/contextMenu';
|
||||
const Resource = require('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) {
|
||||
const iframes = document.getElementsByClassName('tox-edit-area__iframe');
|
||||
if (!iframes.length) return null;
|
||||
|
||||
const iframeRect = iframes[0].getBoundingClientRect();
|
||||
|
||||
if (iframeRect.x < x && iframeRect.y < y && iframeRect.right > x && iframeRect.bottom > y) {
|
||||
const relativeX = x - iframeRect.x;
|
||||
const relativeY = y - iframeRect.y;
|
||||
|
||||
return editor.getDoc().elementFromPoint(relativeX, relativeY);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
interface ContextMenuActionOptions {
|
||||
current: ContextMenuOptions,
|
||||
}
|
||||
|
||||
const contextMenuActionOptions:ContextMenuActionOptions = { current: null };
|
||||
|
||||
export default function(editor:any) {
|
||||
const contextMenuItems = menuItems();
|
||||
|
||||
bridge().window().webContents.on('context-menu', (_event:any, params:any) => {
|
||||
const element = contextMenuElement(editor, params.x, params.y);
|
||||
if (!element) return;
|
||||
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
|
||||
let itemType:ContextMenuItemType = ContextMenuItemType.None;
|
||||
let resourceId = '';
|
||||
let linkToCopy = null;
|
||||
|
||||
if (element.nodeName === 'IMG') {
|
||||
itemType = ContextMenuItemType.Image;
|
||||
resourceId = Resource.pathToId(element.src);
|
||||
} else if (element.nodeName === 'A') {
|
||||
resourceId = Resource.pathToId(element.href);
|
||||
itemType = resourceId ? ContextMenuItemType.Resource : ContextMenuItemType.Link;
|
||||
linkToCopy = element.getAttribute('href') || '';
|
||||
} else {
|
||||
itemType = ContextMenuItemType.Text;
|
||||
}
|
||||
|
||||
contextMenuActionOptions.current = {
|
||||
itemType,
|
||||
resourceId,
|
||||
linkToCopy,
|
||||
textToCopy: null,
|
||||
htmlToCopy: editor.selection ? editor.selection.getContent() : '',
|
||||
insertContent: (content:string) => {
|
||||
editor.insertContent(content);
|
||||
},
|
||||
isReadOnly: false,
|
||||
};
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
for (const itemName in contextMenuItems) {
|
||||
const item = contextMenuItems[itemName];
|
||||
|
||||
if (!item.isActive(itemType, contextMenuActionOptions.current)) continue;
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: item.label,
|
||||
click: () => {
|
||||
item.onAction(contextMenuActionOptions.current);
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(params.misspelledWord, params.dictionarySuggestions);
|
||||
|
||||
for (const item of spellCheckerMenuItems) {
|
||||
menu.append(item);
|
||||
}
|
||||
|
||||
menu.popup();
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import TinyMCE from './NoteBody/TinyMCE/TinyMCE';
|
||||
import CodeMirror from './NoteBody/CodeMirror/CodeMirror';
|
||||
import { connect } from 'react-redux';
|
||||
import MultiNoteActions from '../MultiNoteActions';
|
||||
import NoteToolbar from '../NoteToolbar/NoteToolbar';
|
||||
import { htmlToMarkdown, formNoteToNote } from './utils';
|
||||
import useSearchMarkers from './utils/useSearchMarkers';
|
||||
import useNoteSearchBar from './utils/useNoteSearchBar';
|
||||
@@ -27,15 +25,15 @@ import ToolbarButtonUtils from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import { _ } from 'lib/locale';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
import TagList from '../TagList';
|
||||
import NoteTitleBar from './NoteTitle/NoteTitleBar';
|
||||
import markupLanguageUtils from 'lib/markupLanguageUtils';
|
||||
import usePrevious from 'lib/hooks/usePrevious';
|
||||
import Setting from 'lib/models/Setting';
|
||||
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
const NoteSearchBar = require('../NoteSearchBar.min.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils').default;
|
||||
const usePrevious = require('lib/hooks/usePrevious').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
@@ -45,10 +43,6 @@ const commands = [
|
||||
require('./commands/showRevisions'),
|
||||
];
|
||||
|
||||
const toolbarStyle = {
|
||||
marginBottom: 0,
|
||||
};
|
||||
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
|
||||
|
||||
function NoteEditor(props: NoteEditorProps) {
|
||||
@@ -244,20 +238,20 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const onTitleChange = useCallback((event: any) => onFieldChange('title', event.target.value), [onFieldChange]);
|
||||
|
||||
const onTitleKeydown = useCallback((event:any) => {
|
||||
const keyCode = event.keyCode;
|
||||
// const onTitleKeydown = useCallback((event:any) => {
|
||||
// const keyCode = event.keyCode;
|
||||
|
||||
if (keyCode === 9) {
|
||||
// TAB
|
||||
event.preventDefault();
|
||||
// if (keyCode === 9) {
|
||||
// // TAB
|
||||
// event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', 'noteList');
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', 'noteBody');
|
||||
}
|
||||
}
|
||||
}, [props.dispatch]);
|
||||
// if (event.shiftKey) {
|
||||
// CommandService.instance().execute('focusElement', 'noteList');
|
||||
// } else {
|
||||
// CommandService.instance().execute('focusElement', 'noteBody');
|
||||
// }
|
||||
// }
|
||||
// }, [props.dispatch]);
|
||||
|
||||
const onBodyWillChange = useCallback((event: any) => {
|
||||
handleProvisionalFlag();
|
||||
@@ -351,14 +345,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
return <div style={emptyDivStyle}></div>;
|
||||
}
|
||||
|
||||
function renderNoteToolbar() {
|
||||
return <NoteToolbar
|
||||
themeId={props.themeId}
|
||||
// note={formNote}
|
||||
style={toolbarStyle}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTagButton() {
|
||||
return <ToolbarButton
|
||||
themeId={props.themeId}
|
||||
@@ -377,26 +363,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderTitleBar() {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const titleBarDate = <span style={styles.titleDate}>{time.formatMsToLocal(formNote.user_updated_time)}</span>;
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', height: theme.topRowHeight }}>
|
||||
<input
|
||||
type="text"
|
||||
ref={titleInputRef}
|
||||
placeholder={props.isProvisional ? _('Creating new %s...', formNote.is_todo ? _('to-do') : _('note')) : ''}
|
||||
style={styles.titleInput}
|
||||
onChange={onTitleChange}
|
||||
onKeyDown={onTitleKeydown}
|
||||
value={formNote.title}
|
||||
/>
|
||||
{titleBarDate}
|
||||
{renderNoteToolbar()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
|
||||
|
||||
const editorProps:NoteBodyEditorProps = {
|
||||
@@ -546,7 +512,15 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
<div style={styles.root} onDrop={onDrop}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
{renderResourceWatchingNotification()}
|
||||
{renderTitleBar()}
|
||||
<NoteTitleBar
|
||||
titleInputRef={titleInputRef}
|
||||
themeId={props.themeId}
|
||||
isProvisional={props.isProvisional}
|
||||
noteIsTodo={formNote.is_todo}
|
||||
noteTitle={formNote.title}
|
||||
noteUserUpdatedTime={formNote.user_updated_time}
|
||||
onTitleChange={onTitleChange}
|
||||
/>
|
||||
{renderSearchInfo()}
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
{editor}
|
||||
|
||||
98
ElectronClient/gui/NoteEditor/NoteTitle/NoteTitleBar.tsx
Normal file
98
ElectronClient/gui/NoteEditor/NoteTitle/NoteTitleBar.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import * as React from 'react';
|
||||
import { _ } from 'lib/locale';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import { ChangeEvent, useCallback } from 'react';
|
||||
import NoteToolbar from '../../NoteToolbar/NoteToolbar';
|
||||
import { buildStyle } from 'lib/theme';
|
||||
import time from 'lib/time';
|
||||
|
||||
interface Props {
|
||||
themeId: number,
|
||||
noteUserUpdatedTime: number,
|
||||
noteTitle: string,
|
||||
noteIsTodo: number,
|
||||
isProvisional: boolean,
|
||||
titleInputRef: any,
|
||||
onTitleChange(event: ChangeEvent<HTMLInputElement>):void,
|
||||
}
|
||||
|
||||
function styles_(props: Props) {
|
||||
return buildStyle(['NoteEditorTitleBar'], props.themeId, (theme: any) => {
|
||||
return {
|
||||
root: {
|
||||
display: 'flex', flexDirection: 'row', alignItems: 'center', height: theme.topRowHeight,
|
||||
},
|
||||
titleInput: {
|
||||
flex: 1,
|
||||
display: 'inline-block',
|
||||
paddingTop: 5,
|
||||
minHeight: 35,
|
||||
boxSizing: 'border-box',
|
||||
fontWeight: 'bold',
|
||||
paddingBottom: 5,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 8,
|
||||
marginLeft: 5,
|
||||
color: theme.textStyle.color,
|
||||
fontSize: Math.round(theme.textStyle.fontSize * 1.5),
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: 'none',
|
||||
},
|
||||
|
||||
titleDate: {
|
||||
...theme.textStyle,
|
||||
color: theme.colorFaded,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
},
|
||||
toolbarStyle: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default function NoteTitleBar(props:Props) {
|
||||
const styles = styles_(props);
|
||||
|
||||
const onTitleKeydown = useCallback((event:any) => {
|
||||
const keyCode = event.keyCode;
|
||||
|
||||
if (keyCode === 9) { // TAB
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', 'noteList');
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', 'noteBody');
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
function renderTitleBarDate() {
|
||||
return <span style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
|
||||
}
|
||||
|
||||
function renderNoteToolbar() {
|
||||
return <NoteToolbar
|
||||
themeId={props.themeId}
|
||||
style={styles.toolbarStyle}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.root}>
|
||||
<input
|
||||
type="text"
|
||||
ref={props.titleInputRef}
|
||||
placeholder={props.isProvisional ? _('Creating new %s...', props.noteIsTodo ? _('to-do') : _('note')) : ''}
|
||||
style={styles.titleInput}
|
||||
onChange={props.onTitleChange}
|
||||
onKeyDown={onTitleKeydown}
|
||||
value={props.noteTitle}
|
||||
/>
|
||||
{renderTitleBarDate()}
|
||||
{renderNoteToolbar()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import shim from 'lib/shim';
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
|
||||
|
||||
@@ -4,7 +4,7 @@ import contextMenu from './contextMenu';
|
||||
import ResourceEditWatcher from '../../../lib/services/ResourceEditWatcher/index';
|
||||
import { _ } from 'lib/locale';
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const { urlDecode } = require('lib/string-utils');
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||
import { FormNote, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations from '../commands/editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from 'lib/services/CommandService';
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
const commandsWithDependencies = [
|
||||
|
||||
@@ -5,9 +5,9 @@ import { _ } from 'lib/locale';
|
||||
const { ItemList } = require('../ItemList.min.js');
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Note = require('lib/models/Note');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const React = require('react');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const DialogButtonRow = require('./DialogButtonRow.min');
|
||||
const Datetime = require('react-datetime');
|
||||
const Note = require('lib/models/Note');
|
||||
|
||||
@@ -4,14 +4,14 @@ const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale');
|
||||
const NoteTextViewer = require('./NoteTextViewer').default;
|
||||
const HelpButton = require('./HelpButton.min');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Revision = require('lib/models/Revision');
|
||||
const urlUtils = require('lib/urlUtils');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const RevisionService = require('lib/services/RevisionService');
|
||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const ReactTooltip = require('react-tooltip');
|
||||
const { urlDecode, substrWithEllipsis } = require('lib/string-utils');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
class NoteStatusBarComponent extends React.Component {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const React = require('react');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const Datetime = require('react-datetime');
|
||||
const CreatableSelect = require('react-select/lib/Creatable').default;
|
||||
const Select = require('react-select').default;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { _ } from 'lib/locale';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
||||
37
ElectronClient/gui/menuCommandNames.ts
Normal file
37
ElectronClient/gui/menuCommandNames.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export default function() {
|
||||
return [
|
||||
'attachFile',
|
||||
'copyDevCommand',
|
||||
'exportPdf',
|
||||
'focusElementNoteBody',
|
||||
'focusElementNoteList',
|
||||
'focusElementNoteTitle',
|
||||
'focusElementSideBar',
|
||||
'focusSearch',
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
'insertDateTime',
|
||||
'newFolder',
|
||||
'newNote',
|
||||
'newSubFolder',
|
||||
'newTodo',
|
||||
'openProfileDirectory',
|
||||
'print',
|
||||
'setTags',
|
||||
'showLocalSearch',
|
||||
'showNoteContentProperties',
|
||||
'synchronize',
|
||||
'textBold',
|
||||
'textCode',
|
||||
'textCopy',
|
||||
'textCut',
|
||||
'textItalic',
|
||||
'textLink',
|
||||
'textPaste',
|
||||
'textSelectAll',
|
||||
'toggleExternalEditing',
|
||||
'toggleNoteList',
|
||||
'toggleSideBar',
|
||||
'toggleVisiblePanes',
|
||||
];
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import InteropServiceHelper from '../../InteropServiceHelper';
|
||||
import { _ } from 'lib/locale';
|
||||
import { MenuItemLocation } from 'lib/services/plugins/api/types';
|
||||
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
|
||||
2
ElectronClient/package-lock.json
generated
2
ElectronClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.10",
|
||||
"version": "1.3.11",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.10",
|
||||
"version": "1.3.11",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -8,7 +8,7 @@ const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Tag = require('lib/models/Tag');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Note = require('lib/models/Note');
|
||||
@@ -557,7 +557,7 @@ GotoAnything.manifest = {
|
||||
menuItems: [
|
||||
{
|
||||
name: 'main',
|
||||
parent: 'tools',
|
||||
parent: 'go',
|
||||
label: _('Goto Anything...'),
|
||||
accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'),
|
||||
screens: ['Main'],
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Provides spell checking feature via the native Electron built-in spell checker
|
||||
|
||||
import SpellCheckerServiceDriverBase from 'lib/services/spellChecker/SpellCheckerServiceDriverBase';
|
||||
import bridge from '../bridge';
|
||||
|
||||
export default class SpellCheckerServiceDriverNative extends SpellCheckerServiceDriverBase {
|
||||
|
||||
private session():any {
|
||||
return bridge().window().webContents.session;
|
||||
}
|
||||
|
||||
public get availableLanguages():string[] {
|
||||
return this.session().availableSpellCheckerLanguages;
|
||||
}
|
||||
|
||||
// Language can be set to '' to disable spell-checking
|
||||
public setLanguage(v:string) {
|
||||
// If we pass an empty array, it disables spell checking
|
||||
// https://github.com/electron/electron/issues/25228
|
||||
this.session().setSpellCheckerLanguages(v ? [v] : []);
|
||||
}
|
||||
|
||||
public get language():string {
|
||||
const languages = this.session().getSpellCheckerLanguages();
|
||||
return languages.length ? languages[0] : '';
|
||||
}
|
||||
|
||||
public makeMenuItem(item:any):any {
|
||||
const MenuItem = bridge().MenuItem;
|
||||
return new MenuItem(item);
|
||||
}
|
||||
|
||||
public addWordToSpellCheckerDictionary(_language:string, word:string) {
|
||||
// Actually on Electron all languages share the same dictionary, or
|
||||
// perhaps it's added to the currently active language.
|
||||
this.session().addWordToSpellCheckerDictionary(word);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,14 +11,14 @@ const { defaultState, stateUtils } = require('lib/reducer');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
|
||||
const reduxSharedMiddleware = require('lib/components/shared/reduxSharedMiddleware');
|
||||
const os = require('os');
|
||||
|
||||
@@ -1,22 +1,89 @@
|
||||
import paginationToSql from './models/utils/paginationToSql';
|
||||
|
||||
const { Database } = require('lib/database.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
// New code should make use of this enum
|
||||
export enum ModelType {
|
||||
Note = 1,
|
||||
Folder = 2,
|
||||
Setting = 3,
|
||||
Resource = 4,
|
||||
Tag = 5,
|
||||
NoteTag = 6,
|
||||
Search = 7,
|
||||
Alarm = 8,
|
||||
MasterKey = 9,
|
||||
ItemChange = 10,
|
||||
NoteResource = 11,
|
||||
ResourceLocalState = 12,
|
||||
Revision = 13,
|
||||
Migration = 14,
|
||||
SmartFilter = 15,
|
||||
Command = 16,
|
||||
}
|
||||
|
||||
class BaseModel {
|
||||
static modelType() {
|
||||
|
||||
// TODO: This ancient part of Joplin about model types is a bit of a
|
||||
// mess and should be refactored properly.
|
||||
|
||||
public static typeEnum_:any[] = [
|
||||
['TYPE_NOTE', ModelType.Note],
|
||||
['TYPE_FOLDER', ModelType.Folder],
|
||||
['TYPE_SETTING', ModelType.Setting],
|
||||
['TYPE_RESOURCE', ModelType.Resource],
|
||||
['TYPE_TAG', ModelType.Tag],
|
||||
['TYPE_NOTE_TAG', ModelType.NoteTag],
|
||||
['TYPE_SEARCH', ModelType.Search],
|
||||
['TYPE_ALARM', ModelType.Alarm],
|
||||
['TYPE_MASTER_KEY', ModelType.MasterKey],
|
||||
['TYPE_ITEM_CHANGE', ModelType.ItemChange],
|
||||
['TYPE_NOTE_RESOURCE', ModelType.NoteResource],
|
||||
['TYPE_RESOURCE_LOCAL_STATE', ModelType.ResourceLocalState],
|
||||
['TYPE_REVISION', ModelType.Revision],
|
||||
['TYPE_MIGRATION', ModelType.Migration],
|
||||
['TYPE_SMART_FILTER', ModelType.SmartFilter],
|
||||
['TYPE_COMMAND', ModelType.Command],
|
||||
]
|
||||
|
||||
public static TYPE_NOTE = ModelType.Note;
|
||||
public static TYPE_FOLDER = ModelType.Folder;
|
||||
public static TYPE_SETTING = ModelType.Setting;
|
||||
public static TYPE_RESOURCE = ModelType.Resource;
|
||||
public static TYPE_TAG = ModelType.Tag;
|
||||
public static TYPE_NOTE_TAG = ModelType.NoteTag;
|
||||
public static TYPE_SEARCH = ModelType.Search;
|
||||
public static TYPE_ALARM = ModelType.Alarm;
|
||||
public static TYPE_MASTER_KEY = ModelType.MasterKey;
|
||||
public static TYPE_ITEM_CHANGE = ModelType.ItemChange;
|
||||
public static TYPE_NOTE_RESOURCE = ModelType.NoteResource;
|
||||
public static TYPE_RESOURCE_LOCAL_STATE = ModelType.ResourceLocalState;
|
||||
public static TYPE_REVISION = ModelType.Revision;
|
||||
public static TYPE_MIGRATION = ModelType.Migration;
|
||||
public static TYPE_SMART_FILTER = ModelType.SmartFilter;
|
||||
public static TYPE_COMMAND = ModelType.Command;
|
||||
|
||||
protected static dispatch:Function = function() {};
|
||||
private static saveMutexes_:any = {};
|
||||
|
||||
private static db_:any;
|
||||
|
||||
static modelType():ModelType {
|
||||
throw new Error('Must be overriden');
|
||||
}
|
||||
|
||||
static tableName() {
|
||||
static tableName():string {
|
||||
throw new Error('Must be overriden');
|
||||
}
|
||||
|
||||
static setDb(db) {
|
||||
static setDb(db:any) {
|
||||
this.db_ = db;
|
||||
}
|
||||
|
||||
static addModelMd(model) {
|
||||
static addModelMd(model:any):any {
|
||||
if (!model) return model;
|
||||
|
||||
if (Array.isArray(model)) {
|
||||
@@ -40,29 +107,29 @@ class BaseModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
static byId(items, id) {
|
||||
static byId(items:any[], id:string) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].id == id) return items[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static defaultValues(fieldNames) {
|
||||
const output = {};
|
||||
static defaultValues(fieldNames:string[]) {
|
||||
const output:any = {};
|
||||
for (const n of fieldNames) {
|
||||
output[n] = this.db().fieldDefaultValue(this.tableName(), n);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static modelIndexById(items, id) {
|
||||
static modelIndexById(items:any[], id:string) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].id == id) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static modelsByIds(items, ids) {
|
||||
static modelsByIds(items:any[], ids:string[]) {
|
||||
const output = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (ids.indexOf(items[i].id) >= 0) {
|
||||
@@ -74,14 +141,14 @@ class BaseModel {
|
||||
|
||||
// Prefer the use of this function to compare IDs as it handles the case where
|
||||
// one ID is null and the other is "", in which case they are actually considered to be the same.
|
||||
static idsEqual(id1, id2) {
|
||||
static idsEqual(id1:string, id2:string) {
|
||||
if (!id1 && !id2) return true;
|
||||
if (!id1 && !!id2) return false;
|
||||
if (!!id1 && !id2) return false;
|
||||
return id1 === id2;
|
||||
}
|
||||
|
||||
static modelTypeToName(type) {
|
||||
static modelTypeToName(type:number) {
|
||||
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
const e = BaseModel.typeEnum_[i];
|
||||
if (e[1] === type) return e[0].substr(5).toLowerCase();
|
||||
@@ -89,7 +156,7 @@ class BaseModel {
|
||||
throw new Error(`Unknown model type: ${type}`);
|
||||
}
|
||||
|
||||
static modelNameToType(name) {
|
||||
static modelNameToType(name:string) {
|
||||
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
const e = BaseModel.typeEnum_[i];
|
||||
const eName = e[0].substr(5).toLowerCase();
|
||||
@@ -98,12 +165,12 @@ class BaseModel {
|
||||
throw new Error(`Unknown model name: ${name}`);
|
||||
}
|
||||
|
||||
static hasField(name) {
|
||||
static hasField(name:string) {
|
||||
const fields = this.fieldNames();
|
||||
return fields.indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
static fieldNames(withPrefix = false) {
|
||||
static fieldNames(withPrefix:boolean = false) {
|
||||
const output = this.db().tableFieldNames(this.tableName());
|
||||
if (!withPrefix) return output;
|
||||
|
||||
@@ -116,7 +183,7 @@ class BaseModel {
|
||||
return temp;
|
||||
}
|
||||
|
||||
static fieldType(name, defaultValue = null) {
|
||||
static fieldType(name:string, defaultValue:any = null) {
|
||||
const fields = this.fields();
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (fields[i].name == name) return fields[i].type;
|
||||
@@ -129,8 +196,8 @@ class BaseModel {
|
||||
return this.db().tableFields(this.tableName());
|
||||
}
|
||||
|
||||
static removeUnknownFields(model) {
|
||||
const newModel = {};
|
||||
static removeUnknownFields(model:any) {
|
||||
const newModel:any = {};
|
||||
for (const n in model) {
|
||||
if (!model.hasOwnProperty(n)) continue;
|
||||
if (!this.hasField(n) && n !== 'type_') continue;
|
||||
@@ -141,7 +208,7 @@ class BaseModel {
|
||||
|
||||
static new() {
|
||||
const fields = this.fields();
|
||||
const output = {};
|
||||
const output:any = {};
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const f = fields[i];
|
||||
output[f.name] = f.default;
|
||||
@@ -149,7 +216,7 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static modOptions(options) {
|
||||
static modOptions(options:any) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
} else {
|
||||
@@ -161,42 +228,42 @@ class BaseModel {
|
||||
return options;
|
||||
}
|
||||
|
||||
static count(options = null) {
|
||||
static count(options:any = null) {
|
||||
if (!options) options = {};
|
||||
let sql = `SELECT count(*) as total FROM \`${this.tableName()}\``;
|
||||
if (options.where) sql += ` WHERE ${options.where}`;
|
||||
return this.db()
|
||||
.selectOne(sql)
|
||||
.then(r => {
|
||||
.then((r:any) => {
|
||||
return r ? r['total'] : 0;
|
||||
});
|
||||
}
|
||||
|
||||
static load(id, options = null) {
|
||||
static load(id:string, options:any = null) {
|
||||
return this.loadByField('id', id, options);
|
||||
}
|
||||
|
||||
static shortId(id) {
|
||||
static shortId(id:string) {
|
||||
return id.substr(0, 5);
|
||||
}
|
||||
|
||||
static loadByPartialId(partialId) {
|
||||
static loadByPartialId(partialId:string) {
|
||||
return this.modelSelectAll(`SELECT * FROM \`${this.tableName()}\` WHERE \`id\` LIKE ?`, [`${partialId}%`]);
|
||||
}
|
||||
|
||||
static applySqlOptions(options, sql, params = null) {
|
||||
static applySqlOptions(options:any, sql:string, params:any[] = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
if (options.order && options.order.length) {
|
||||
const items = [];
|
||||
for (let i = 0; i < options.order.length; i++) {
|
||||
const o = options.order[i];
|
||||
let item = `\`${o.by}\``;
|
||||
if (options.caseInsensitive === true) item += ' COLLATE NOCASE';
|
||||
if (o.dir) item += ` ${o.dir}`;
|
||||
items.push(item);
|
||||
}
|
||||
sql += ` ORDER BY ${items.join(', ')}`;
|
||||
// const items = [];
|
||||
// for (let i = 0; i < options.order.length; i++) {
|
||||
// const o = options.order[i];
|
||||
// let item = `\`${o.by}\``;
|
||||
// if (options.caseInsensitive === true) item += ' COLLATE NOCASE';
|
||||
// if (o.dir) item += ` ${o.dir}`;
|
||||
// items.push(item);
|
||||
// }
|
||||
sql += ` ORDER BY ${paginationToSql(options)}`;
|
||||
}
|
||||
|
||||
if (options.limit) sql += ` LIMIT ${options.limit}`;
|
||||
@@ -204,18 +271,18 @@ class BaseModel {
|
||||
return { sql: sql, params: params };
|
||||
}
|
||||
|
||||
static async allIds(options = null) {
|
||||
static async allIds(options:any = null) {
|
||||
const q = this.applySqlOptions(options, `SELECT id FROM \`${this.tableName()}\``);
|
||||
const rows = await this.db().selectAll(q.sql, q.params);
|
||||
return rows.map(r => r.id);
|
||||
return rows.map((r:any) => r.id);
|
||||
}
|
||||
|
||||
static async all(options = null) {
|
||||
static async all(options:any = null) {
|
||||
if (!options) options = {};
|
||||
if (!options.fields) options.fields = '*';
|
||||
|
||||
let sql = `SELECT ${this.db().escapeFields(options.fields)} FROM \`${this.tableName()}\``;
|
||||
let params = [];
|
||||
let params:any[] = [];
|
||||
if (options.where) {
|
||||
sql += ` WHERE ${options.where}`;
|
||||
if (options.whereParams) params = params.concat(options.whereParams);
|
||||
@@ -225,7 +292,7 @@ class BaseModel {
|
||||
return this.modelSelectAll(q.sql, q.params);
|
||||
}
|
||||
|
||||
static async byIds(ids, options = null) {
|
||||
static async byIds(ids:string[], options:any = null) {
|
||||
if (!ids.length) return [];
|
||||
if (!options) options = {};
|
||||
if (!options.fields) options.fields = '*';
|
||||
@@ -236,7 +303,7 @@ class BaseModel {
|
||||
return this.modelSelectAll(q.sql);
|
||||
}
|
||||
|
||||
static async search(options = null) {
|
||||
static async search(options:any = null) {
|
||||
if (!options) options = {};
|
||||
if (!options.fields) options.fields = '*';
|
||||
|
||||
@@ -258,25 +325,25 @@ class BaseModel {
|
||||
return this.modelSelectAll(query.sql, query.params);
|
||||
}
|
||||
|
||||
static modelSelectOne(sql, params = null) {
|
||||
static modelSelectOne(sql:string, params:any[] = null) {
|
||||
if (params === null) params = [];
|
||||
return this.db()
|
||||
.selectOne(sql, params)
|
||||
.then(model => {
|
||||
.then((model:any) => {
|
||||
return this.filter(this.addModelMd(model));
|
||||
});
|
||||
}
|
||||
|
||||
static modelSelectAll(sql, params = null) {
|
||||
static modelSelectAll(sql:string, params:any[] = null) {
|
||||
if (params === null) params = [];
|
||||
return this.db()
|
||||
.selectAll(sql, params)
|
||||
.then(models => {
|
||||
.then((models:any[]) => {
|
||||
return this.filterArray(this.addModelMd(models));
|
||||
});
|
||||
}
|
||||
|
||||
static loadByField(fieldName, fieldValue, options = null) {
|
||||
static loadByField(fieldName:string, fieldValue:any, options:any = null) {
|
||||
if (!options) options = {};
|
||||
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
|
||||
if (!options.fields) options.fields = '*';
|
||||
@@ -285,7 +352,7 @@ class BaseModel {
|
||||
return this.modelSelectOne(sql, [fieldValue]);
|
||||
}
|
||||
|
||||
static loadByFields(fields, options = null) {
|
||||
static loadByFields(fields:string[], options:any = null) {
|
||||
if (!options) options = {};
|
||||
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
|
||||
if (!options.fields) options.fields = '*';
|
||||
@@ -300,12 +367,12 @@ class BaseModel {
|
||||
return this.modelSelectOne(sql, params);
|
||||
}
|
||||
|
||||
static loadByTitle(fieldValue) {
|
||||
static loadByTitle(fieldValue:any) {
|
||||
return this.modelSelectOne(`SELECT * FROM \`${this.tableName()}\` WHERE \`title\` = ?`, [fieldValue]);
|
||||
}
|
||||
|
||||
static diffObjects(oldModel, newModel) {
|
||||
const output = {};
|
||||
static diffObjects(oldModel:any, newModel:any) {
|
||||
const output:any = {};
|
||||
const fields = this.diffObjectsFields(oldModel, newModel);
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
output[fields[i]] = newModel[fields[i]];
|
||||
@@ -314,7 +381,7 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static diffObjectsFields(oldModel, newModel) {
|
||||
static diffObjectsFields(oldModel:any, newModel:any) {
|
||||
const output = [];
|
||||
for (const n in newModel) {
|
||||
if (!newModel.hasOwnProperty(n)) continue;
|
||||
@@ -326,15 +393,15 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static modelsAreSame(oldModel, newModel) {
|
||||
static modelsAreSame(oldModel:any, newModel:any) {
|
||||
const diff = this.diffObjects(oldModel, newModel);
|
||||
delete diff.type_;
|
||||
return !Object.getOwnPropertyNames(diff).length;
|
||||
}
|
||||
|
||||
static saveMutex(modelOrId) {
|
||||
static saveMutex(modelOrId:any) {
|
||||
const noLockMutex = {
|
||||
acquire: function() {
|
||||
acquire: function():any {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
@@ -353,7 +420,7 @@ class BaseModel {
|
||||
return mutex;
|
||||
}
|
||||
|
||||
static releaseSaveMutex(modelOrId, release) {
|
||||
static releaseSaveMutex(modelOrId:any, release:Function) {
|
||||
if (!release) return;
|
||||
if (!modelOrId) return release();
|
||||
|
||||
@@ -368,8 +435,8 @@ class BaseModel {
|
||||
release();
|
||||
}
|
||||
|
||||
static saveQuery(o, options) {
|
||||
let temp = {};
|
||||
static saveQuery(o:any, options:any) {
|
||||
let temp:any = {};
|
||||
const fieldNames = this.fieldNames();
|
||||
for (let i = 0; i < fieldNames.length; i++) {
|
||||
const n = fieldNames[i];
|
||||
@@ -381,7 +448,7 @@ class BaseModel {
|
||||
// be part of the final list of fields if autoTimestamp is on.
|
||||
// id also will stay.
|
||||
if (!options.isNew && options.fields) {
|
||||
const filtered = {};
|
||||
const filtered:any = {};
|
||||
for (const k in temp) {
|
||||
if (!temp.hasOwnProperty(k)) continue;
|
||||
if (k !== 'id' && options.fields.indexOf(k) < 0) continue;
|
||||
@@ -393,7 +460,7 @@ class BaseModel {
|
||||
o = temp;
|
||||
|
||||
let modelId = temp.id;
|
||||
let query = {};
|
||||
let query:any = {};
|
||||
|
||||
const timeNow = time.unixMs();
|
||||
|
||||
@@ -445,7 +512,7 @@ class BaseModel {
|
||||
return query;
|
||||
}
|
||||
|
||||
static userSideValidation(o) {
|
||||
static userSideValidation(o:any) {
|
||||
if (o.id && !o.id.match(/^[a-f0-9]{32}$/)) {
|
||||
throw new Error('Validation error: ID must a 32-characters lowercase hexadecimal string');
|
||||
}
|
||||
@@ -456,7 +523,7 @@ class BaseModel {
|
||||
}
|
||||
}
|
||||
|
||||
static async save(o, options = null) {
|
||||
static async save(o:any, options:any = null) {
|
||||
// When saving, there's a mutex per model ID. This is because the model returned from this function
|
||||
// is basically its input `o` (instead of being read from the database, for performance reasons).
|
||||
// This works well in general except if that model is saved simultaneously in two places. In that
|
||||
@@ -526,7 +593,7 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static isNew(object, options) {
|
||||
static isNew(object:any, options:any) {
|
||||
if (options && 'isNew' in options) {
|
||||
// options.isNew can be "auto" too
|
||||
if (options.isNew === true) return true;
|
||||
@@ -536,7 +603,7 @@ class BaseModel {
|
||||
return !object.id;
|
||||
}
|
||||
|
||||
static filterArray(models) {
|
||||
static filterArray(models:any[]) {
|
||||
const output = [];
|
||||
for (let i = 0; i < models.length; i++) {
|
||||
output.push(this.filter(models[i]));
|
||||
@@ -544,7 +611,7 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static filter(model) {
|
||||
static filter(model:any) {
|
||||
if (!model) return model;
|
||||
|
||||
const output = Object.assign({}, model);
|
||||
@@ -567,12 +634,12 @@ class BaseModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
static delete(id) {
|
||||
static delete(id:string) {
|
||||
if (!id) throw new Error('Cannot delete object without an ID');
|
||||
return this.db().exec(`DELETE FROM ${this.tableName()} WHERE id = ?`, [id]);
|
||||
}
|
||||
|
||||
static batchDelete(ids, options = null) {
|
||||
static batchDelete(ids:string[], options:any = null) {
|
||||
if (!ids.length) return;
|
||||
options = this.modOptions(options);
|
||||
const idFieldName = options.idFieldName ? options.idFieldName : 'id';
|
||||
@@ -590,32 +657,9 @@ class BaseModel {
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.typeEnum_ = [
|
||||
['TYPE_NOTE', 1],
|
||||
['TYPE_FOLDER', 2],
|
||||
['TYPE_SETTING', 3],
|
||||
['TYPE_RESOURCE', 4],
|
||||
['TYPE_TAG', 5],
|
||||
['TYPE_NOTE_TAG', 6],
|
||||
['TYPE_SEARCH', 7],
|
||||
['TYPE_ALARM', 8],
|
||||
['TYPE_MASTER_KEY', 9],
|
||||
['TYPE_ITEM_CHANGE', 10],
|
||||
['TYPE_NOTE_RESOURCE', 11],
|
||||
['TYPE_RESOURCE_LOCAL_STATE', 12],
|
||||
['TYPE_REVISION', 13],
|
||||
['TYPE_MIGRATION', 14],
|
||||
['TYPE_SMART_FILTER', 15],
|
||||
['TYPE_COMMAND', 16],
|
||||
];
|
||||
|
||||
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
const e = BaseModel.typeEnum_[i];
|
||||
BaseModel[e[0]] = e[1];
|
||||
(BaseModel as any)[e[0]] = e[1];
|
||||
}
|
||||
|
||||
BaseModel.db_ = null;
|
||||
BaseModel.dispatch = function() {};
|
||||
BaseModel.saveMutexes_ = {};
|
||||
|
||||
module.exports = BaseModel;
|
||||
export default BaseModel;
|
||||
@@ -4,7 +4,7 @@ const Logger = require('lib/Logger').default;
|
||||
const { randomClipperPort, startPort } = require('lib/randomClipperPort');
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
const Api = require('lib/services/rest/Api').default;
|
||||
const ApiResponse = require('lib/services/rest/ApiResponse');
|
||||
const ApiResponse = require('lib/services/rest/ApiResponse').default;
|
||||
const multiparty = require('multiparty');
|
||||
|
||||
class ClipperServer {
|
||||
@@ -29,7 +29,6 @@ class ClipperServer {
|
||||
|
||||
setLogger(l) {
|
||||
this.logger_ = l;
|
||||
this.api_.setLogger(l);
|
||||
}
|
||||
|
||||
logger() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const { time } = require('lib/time-utils');
|
||||
const time = require('lib/time').default;
|
||||
const EventDispatcher = require('lib/EventDispatcher');
|
||||
|
||||
class DropboxApi {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const moment = require('moment');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const { FsDriverDummy } = require('lib/fs-driver-dummy.js');
|
||||
|
||||
export enum TargetType {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// whenever the update() function is called, and in mobile it's called for
|
||||
// example on the Redux action middleware or when the app gets focus.
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
|
||||
type IntervalId = number;
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ const Resource = require('lib/models/Resource.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const ResourceLocalState = require('lib/models/ResourceLocalState.js');
|
||||
const MasterKey = require('lib/models/MasterKey.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseModel = require('lib/BaseModel').default;
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const time = require('lib/time').default;
|
||||
const JoplinError = require('lib/JoplinError');
|
||||
const TaskQueue = require('lib/TaskQueue');
|
||||
const { Dirnames } = require('lib/services/synchronizer/utils/types');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user