1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-27 20:29:45 +02:00

Compare commits

..

25 Commits

Author SHA1 Message Date
Laurent Cozic
051d628250 tsconfig 2023-06-13 11:10:07 +01:00
Laurent Cozic
d8f365834b update 2023-06-13 11:01:45 +01:00
Laurent Cozic
6dc6acc644 update 2023-06-13 10:54:44 +01:00
Laurent Cozic
30e1164df9 Merge branch 'dev' into user_data 2023-06-12 17:31:20 +01:00
Laurent Cozic
3c0331eb6e Desktop: Fixes #8210: Display plugin console in dev mode 2023-06-12 17:30:47 +01:00
Laurent Cozic
a8821dfcb5 test 2023-06-12 17:29:57 +01:00
Laurent Cozic
54b15a1917 Plugins: Updated types 2023-06-12 17:09:09 +01:00
Laurent Cozic
e2d27275db Plugins: Updated types 2023-06-12 17:04:46 +01:00
Laurent Cozic
b90cb89e0f fix 2023-06-12 17:02:49 +01:00
Laurent Cozic
a6516c5f14 Plugins: Updated types 2023-06-12 16:52:09 +01:00
Laurent Cozic
249a744e9d init 2023-06-12 16:49:54 +01:00
Laurent Cozic
1eeb5ab471 Mobile: Improved Vosk error handling 2023-06-11 16:13:36 +01:00
Joplin Bot
c77a0b4709 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-11 12:17:44 +00:00
Laurent Cozic
1b64688042 Doc: Update French translation 2023-06-11 12:20:13 +01:00
Joplin Bot
c082505f61 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-11 00:46:39 +00:00
Laurent Cozic
f555e528ba All: Resolves #7686: Upgrade E2EE encryption method to AES-256 2023-06-10 19:20:08 +01:00
Joplin Bot
96ff76fd1d Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-10 18:12:20 +00:00
Laurent Cozic
9ceb7b9c88 Mobile: Upgrade react-native-webview to v12 2023-06-10 17:58:55 +01:00
Marph
6dc8ad2ba6 Desktop: Resolves #3535: Configure Rich Text editor to handle the first table row as header (#8163) 2023-06-10 17:08:15 +01:00
renovate[bot]
152e354d87 Update dependency highlight.js to v11.8.0 (#8296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-10 17:07:35 +01:00
Laurent Cozic
dbef391855 Disable RN auto-updates 2023-06-10 17:07:07 +01:00
Laurent Cozic
78f3f1c01a Mobile: Fixed text update issue when attaching a file to an empty note 2023-06-10 17:04:45 +01:00
Laurent Cozic
e7409145b6 Mobile: Upgrade to React Native 0.71 2023-06-10 17:03:34 +01:00
Calum Lind
bf8e34a0be Linux: Fixes #8297: Fix corrupted sidebar (#8298) 2023-06-10 14:03:26 +01:00
renovate[bot]
4aaea3fe71 Update dependency @react-native-community/datetimepicker to v7.0.1 (#8295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 18:15:23 +00:00
118 changed files with 15764 additions and 2919 deletions

View File

@@ -557,6 +557,8 @@ packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/types.js
packages/lib/models/utils/userData.js
packages/lib/models/utils/userData.test.js
packages/lib/net-utils.js
packages/lib/ntp.js
packages/lib/onedrive-api.js

2
.gitignore vendored
View File

@@ -542,6 +542,8 @@ packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/types.js
packages/lib/models/utils/userData.js
packages/lib/models/utils/userData.test.js
packages/lib/net-utils.js
packages/lib/ntp.js
packages/lib/onedrive-api.js

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -20,3 +20,4 @@ cd "$SCRIPT_DIR/withExternalModules/" && yo joplin --update --skip-install --sil
cd "$SCRIPT_DIR/post_messages/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/nativeModule/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/external_assets/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/user_data/" && yo joplin --update --skip-install --silent

View File

@@ -0,0 +1,3 @@
dist/
node_modules/
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -0,0 +1,72 @@
# generator-joplin
Scaffolds out a new Joplin plugin
## Installation
First, install [Yeoman](http://yeoman.io) and generator-joplin using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
```bash
npm install -g yo
npm install -g generator-joplin
```
Then generate your new project:
```bash
yo joplin
```
## Development
To test the generator for development purposes, follow the instructions there: https://yeoman.io/authoring/#running-the-generator
This is a template to create a new Joplin plugin.
## Structure
The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
The file `/plugin.config.json` could also be useful if you intend to use [external scripts](#external-script-files), such as content scripts or webview scripts.
## Building the plugin
The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin.
To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `npm run update`.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
## External script files
By default, the compiler (webpack) is going to compile `src/index.ts` only (as well as any file it imports), and any other file will simply be copied to the plugin package. In some cases this is sufficient, however if you have [content scripts](https://joplinapp.org/api/references/plugin_api/classes/joplincontentscripts.html) or [webview scripts](https://joplinapp.org/api/references/plugin_api/classes/joplinviewspanels.html#addscript) you might want to compile them too, in particular in these two cases:
- The script is a TypeScript file - in which case it has to be compiled to JavaScript.
- The script requires modules you've added to package.json. In that case, the script, whether JS or TS, must be compiled so that the dependencies are bundled with the JPL file.
To get such an external script file to compile, you need to add it to the `extraScripts` array in `plugin.config.json`. The path you add should be relative to /src. For example, if you have a file in "/src/webviews/index.ts", the path should be set to "webviews/index.ts". Once compiled, the file will always be named with a .js extension. So you will get "webviews/index.js" in the plugin package, and that's the path you should use to reference the file.
## License
MIT © Laurent Cozic

View File

@@ -0,0 +1,24 @@
# Joplin Plugin
This is a template to create a new Joplin plugin.
The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
## Building the plugin
The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin.
To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Updating the plugin framework
To update the plugin framework, run `npm run update`.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.

View File

@@ -0,0 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
get process(): any;
}

View File

@@ -0,0 +1,68 @@
import Plugin from '../Plugin';
import JoplinData from './JoplinData';
import JoplinPlugins from './JoplinPlugins';
import JoplinWorkspace from './JoplinWorkspace';
import JoplinFilters from './JoplinFilters';
import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import JoplinContentScripts from './JoplinContentScripts';
import JoplinClipboard from './JoplinClipboard';
import JoplinWindow from './JoplinWindow';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*
* The API is now relatively stable and in general maintaining backward compatibility is a top priority, so you shouldn't except much breakages.
*
* If a breaking change ever becomes needed, best effort will be done to:
*
* - Deprecate features instead of removing them, so as to give you time to fix the issue;
* - Document breaking changes in the changelog;
*
* So if you are developing a plugin, please keep an eye on the changelog as everything will be in there with information about how to update your code.
*/
export default class Joplin {
private data_;
private plugins_;
private workspace_;
private filters_;
private commands_;
private views_;
private interop_;
private settings_;
private contentScripts_;
private clipboard_;
private window_;
constructor(implementation: any, plugin: Plugin, store: any);
get data(): JoplinData;
get clipboard(): JoplinClipboard;
get window(): JoplinWindow;
get plugins(): JoplinPlugins;
get workspace(): JoplinWorkspace;
get contentScripts(): JoplinContentScripts;
/**
* @ignore
*
* Not sure if it's the best way to hook into the app
* so for now disable filters.
*/
get filters(): JoplinFilters;
get commands(): JoplinCommands;
get views(): JoplinViews;
get interop(): JoplinInterop;
get settings(): JoplinSettings;
/**
* It is not possible to bundle native packages with a plugin, because they
* need to work cross-platforms. Instead access to certain useful native
* packages is provided using this function.
*
* Currently these packages are available:
*
* - [sqlite3](https://www.npmjs.com/package/sqlite3)
* - [fs-extra](https://www.npmjs.com/package/fs-extra)
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/nativeModule)
*/
require(_path: string): any;
}

View File

@@ -0,0 +1,23 @@
export default class JoplinClipboard {
private electronClipboard_;
private electronNativeImage_;
constructor(electronClipboard: any, electronNativeImage: any);
readText(): Promise<string>;
writeText(text: string): Promise<void>;
readHtml(): Promise<string>;
writeHtml(html: string): Promise<void>;
/**
* Returns the image in [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
*/
readImage(): Promise<string>;
/**
* Takes an image in [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
*/
writeImage(dataUrl: string): Promise<void>;
/**
* Returns the list available formats (mime types).
*
* For example [ 'text/plain', 'text/html' ]
*/
availableFormats(): Promise<string[]>;
}

View File

@@ -0,0 +1,89 @@
import { Command } from './types';
/**
* This class allows executing or registering new Joplin commands. Commands
* can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or
* {@link JoplinViewsMenuItems | menu items}.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*
* ## Executing Joplin's internal commands
*
* It is also possible to execute internal Joplin's commands which, as of
* now, are not well documented. You can find the list directly on GitHub
* though at the following locations:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts)
*
* To view what arguments are supported, you can open any of these files
* and look at the `execute()` command.
*
* ## Executing editor commands
*
* There might be a situation where you want to invoke editor commands
* without using a {@link JoplinContentScripts | contentScript}. For this
* reason Joplin provides the built in `editor.execCommand` command.
*
* `editor.execCommand` should work with any core command in both the
* [CodeMirror](https://codemirror.net/doc/manual.html#execCommand) and
* [TinyMCE](https://www.tiny.cloud/docs/api/tinymce/tinymce.editorcommands/#execcommand) editors,
* as well as most functions calls directly on a CodeMirror editor object (extensions).
*
* * [CodeMirror commands](https://codemirror.net/doc/manual.html#commands)
* * [TinyMCE core editor commands](https://www.tiny.cloud/docs/advanced/editor-command-identifiers/#coreeditorcommands)
*
* `editor.execCommand` supports adding arguments for the commands.
*
* ```typescript
* await joplin.commands.execute('editor.execCommand', {
* name: 'madeUpCommand', // CodeMirror and TinyMCE
* args: [], // CodeMirror and TinyMCE
* ui: false, // TinyMCE only
* value: '', // TinyMCE only
* });
* ```
*
* [View the example using the CodeMirror editor](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/tests/support/plugins/codemirror_content_script/src/index.ts)
*
*/
export default class JoplinCommands {
/**
* <span class="platform-desktop">desktop</span> Executes the given
* command.
*
* The command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
*
* ```typescript
* // Create a new note in the current notebook:
* await joplin.commands.execute('newNote');
*
* // Create a new sub-notebook under the provided notebook
* // Note: internally, notebooks are called "folders".
* await joplin.commands.execute('newFolder', "SOME_FOLDER_ID");
* ```
*/
execute(commandName: string, ...args: any[]): Promise<any | void>;
/**
* <span class="platform-desktop">desktop</span> Registers a new command.
*
* ```typescript
* // Register a new commmand called "testCommand1"
*
* await joplin.commands.register({
* name: 'testCommand1',
* label: 'My Test Command 1',
* iconName: 'fas fa-music',
* execute: () => {
* alert('Testing plugin command 1');
* },
* });
* ```
*/
register(command: Command): Promise<void>;
}

View File

@@ -0,0 +1,40 @@
import Plugin from '../Plugin';
import { ContentScriptType } from './types';
export default class JoplinContentScripts {
private plugin;
constructor(plugin: Plugin);
/**
* Registers a new content script. Unlike regular plugin code, which runs in
* a separate process, content scripts run within the main process code and
* thus allow improved performances and more customisations in specific
* cases. It can be used for example to load a Markdown or editor plugin.
*
* Note that registering a content script in itself will do nothing - it
* will only be loaded in specific cases by the relevant app modules (eg.
* the Markdown renderer or the code editor). So it is not a way to inject
* and run arbitrary code in the app, which for safety and performance
* reasons is not supported.
*
* The plugin generator provides a way to build any content script you might
* want to package as well as its dependencies. See the [Plugin Generator
* doc](https://github.com/laurent22/joplin/blob/dev/packages/generator-joplin/README.md)
* for more information.
*
* * [View the renderer demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* * [View the editor demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
*
* See also the [postMessage demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.
* @param scriptPath Must be a path relative to the plugin main script. For example, if your file content_script.js is next to your index.ts file, you would set `scriptPath` to `"./content_script.js`.
*/
register(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
/**
* Listens to a messages sent from the content script using postMessage().
* See {@link ContentScriptType} for more information as well as the
* [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*/
onMessage(contentScriptId: string, callback: any): Promise<void>;
}

View File

@@ -0,0 +1,50 @@
import { ModelType } from '../../../BaseModel';
import { Path } from './types';
/**
* This module provides access to the Joplin data API: https://joplinapp.org/api/references/rest_api/
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
* or to update them or delete them.
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
*
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
* And each method takes these parameters:
*
* * `path`: This is an array that represents the path to the resource in the form `["resouceName", "resourceId", "resourceLink"]` (eg. ["tags", ":id", "notes"]). The "resources" segment is the name of the resources you want to access (eg. "notes", "folders", etc.). If not followed by anything, it will refer to all the resources in that collection. The optional "resourceId" points to a particular resources within the collection. Finally, an optional "link" can be present, which links the resource to a collection of resources. This can be used in the API for example to retrieve all the notes associated with a tag.
* * `query`: (Optional) The query parameters. In a URL, this is the part after the question mark "?". In this case, it should be an object with key/value pairs.
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
* * `files`: (Optional) Used to create new resources and associate them with files.
*
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/references/rest_api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
*
* For example:
*
* ```typescript
* // Get a note ID, title and body
* const noteId = 'some_note_id';
* const note = await joplin.data.get(['notes', noteId], { fields: ['id', 'title', 'body'] });
*
* // Get all folders
* const folders = await joplin.data.get(['folders']);
*
* // Set the note body
* await joplin.data.put(['notes', noteId], null, { body: "New note body" });
*
* // Create a new note under one of the folders
* await joplin.data.post(['notes'], null, { body: "my new note", title: "some title", parent_id: folders[0].id });
* ```
*/
export default class JoplinData {
private api_;
private pathSegmentRegex_;
private serializeApiBody;
private pathToString;
get(path: Path, query?: any): Promise<any>;
post(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
put(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
delete(path: Path, query?: any): Promise<any>;
itemType(itemId: string): Promise<ModelType>;
resourcePath(resourceId: string): Promise<string>;
}

View File

@@ -0,0 +1,10 @@
/**
* @ignore
*
* Not sure if it's the best way to hook into the app
* so for now disable filters.
*/
export default class JoplinFilters {
on(name: string, callback: Function): Promise<void>;
off(name: string, callback: Function): Promise<void>;
}

View File

@@ -0,0 +1,17 @@
import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.
*
* See the documentation of the [[ExportModule]] and [[ImportModule]] for more information.
*
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
*/
export default class JoplinInterop {
registerExportModule(module: ExportModule): Promise<void>;
registerImportModule(module: ImportModule): Promise<void>;
}

View File

@@ -0,0 +1,43 @@
import Plugin from '../Plugin';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
*/
export default class JoplinPlugins {
private plugin;
constructor(plugin: Plugin);
/**
* Registers a new plugin. This is the entry point when creating a plugin. You should pass a simple object with an `onStart` method to it.
* That `onStart` method will be executed as soon as the plugin is loaded.
*
* ```typescript
* joplin.plugins.register({
* onStart: async function() {
* // Run your plugin code here
* }
* });
* ```
*/
register(script: Script): Promise<void>;
/**
* @deprecated Use joplin.contentScripts.register()
*/
registerContentScript(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
/**
* Gets the plugin own data directory path. Use this to store any
* plugin-related data. Unlike [[installationDir]], any data stored here
* will be persisted.
*/
dataDir(): Promise<string>;
/**
* Gets the plugin installation directory. This can be used to access any
* asset that was packaged with the plugin. This directory should be
* considered read-only because any data you store here might be deleted or
* re-created at any time. To store new persistent data, use [[dataDir]].
*/
installationDir(): Promise<string>;
/**
* @deprecated Use joplin.require()
*/
require(_path: string): any;
}

View File

@@ -0,0 +1,64 @@
import Plugin from '../Plugin';
import { SettingItem, SettingSection } from './types';
export interface ChangeEvent {
/**
* Setting keys that have been changed
*/
keys: string[];
}
export declare type ChangeHandler = (event: ChangeEvent) => void;
/**
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
*
* Settings are essentially key/value pairs.
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
constructor(plugin: Plugin);
private get keyPrefix();
private namespacedKey;
/**
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.
*/
registerSection(name: string, section: SettingSection): Promise<void>;
/**
* Gets a setting value (only applies to setting you registered from your plugin)
*/
value(key: string): Promise<any>;
/**
* Sets a setting value (only applies to setting you registered from your plugin)
*/
setValue(key: string, value: any): Promise<void>;
/**
* Gets a global setting value, including app-specific settings and those set by other plugins.
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
/**
* Called when one or multiple settings of your plugin have been changed.
* - For performance reasons, this event is triggered with a delay.
* - You will only get events for your own plugin settings.
*/
onChange(handler: ChangeHandler): Promise<void>;
}

View File

@@ -0,0 +1,28 @@
import Plugin from '../Plugin';
import JoplinViewsDialogs from './JoplinViewsDialogs';
import JoplinViewsMenuItems from './JoplinViewsMenuItems';
import JoplinViewsMenus from './JoplinViewsMenus';
import JoplinViewsToolbarButtons from './JoplinViewsToolbarButtons';
import JoplinViewsPanels from './JoplinViewsPanels';
/**
* This namespace provides access to view-related services.
*
* All view services provide a `create()` method which you would use to create the view object, whether it's a dialog, a toolbar button or a menu item.
* In some cases, the `create()` method will return a [[ViewHandle]], which you would use to act on the view, for example to set certain properties or call some methods.
*/
export default class JoplinViews {
private store;
private plugin;
private dialogs_;
private panels_;
private menuItems_;
private menus_;
private toolbarButtons_;
private implementation_;
constructor(implementation: any, plugin: Plugin, store: any);
get dialogs(): JoplinViewsDialogs;
get panels(): JoplinViewsPanels;
get menuItems(): JoplinViewsMenuItems;
get menus(): JoplinViewsMenus;
get toolbarButtons(): JoplinViewsToolbarButtons;
}

View File

@@ -0,0 +1,68 @@
import Plugin from '../Plugin';
import { ButtonSpec, ViewHandle, DialogResult } from './types';
/**
* Allows creating and managing dialogs. A dialog is modal window that
* contains a webview and a row of buttons. You can update the
* webview using the `setHtml` method. Dialogs are hidden by default and
* you need to call `open()` to open them. Once the user clicks on a
* button, the `open` call will return an object indicating what button was
* clicked on.
*
* ## Retrieving form values
*
* If your HTML content included one or more forms, a `formData` object
* will also be included with the key/value for each form.
*
* ## Special button IDs
*
* The following buttons IDs have a special meaning:
*
* - `ok`, `yes`, `submit`, `confirm`: They are considered "submit" buttons
* - `cancel`, `no`, `reject`: They are considered "dismiss" buttons
*
* This information is used by the application to determine what action
* should be done when the user presses "Enter" or "Escape" within the
* dialog. If they press "Enter", the first "submit" button will be
* automatically clicked. If they press "Escape" the first "dismiss" button
* will be automatically clicked.
*
* [View the demo
* plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
private plugin;
private implementation_;
constructor(implementation: any, plugin: Plugin, store: any);
private controller;
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/
showMessageBox(message: string): Promise<number>;
/**
* Sets the dialog HTML content
*/
setHtml(handle: ViewHandle, html: string): Promise<string>;
/**
* Adds and loads a new JS or CSS files into the dialog.
*/
addScript(handle: ViewHandle, scriptPath: string): Promise<void>;
/**
* Sets the dialog buttons.
*/
setButtons(handle: ViewHandle, buttons: ButtonSpec[]): Promise<ButtonSpec[]>;
/**
* Opens the dialog
*/
open(handle: ViewHandle): Promise<DialogResult>;
/**
* Toggle on whether to fit the dialog size to the content or not.
* When set to false, the dialog is set to 90vw and 80vh
* @default true
*/
setFitToContent(handle: ViewHandle, status: boolean): Promise<boolean>;
}

View File

@@ -0,0 +1,16 @@
import { CreateMenuItemOptions, MenuItemLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -0,0 +1,18 @@
import { MenuItem, MenuItemLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
private registerCommandAccelerators;
/**
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -0,0 +1,78 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
private controller;
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/
setHtml(handle: ViewHandle, html: string): Promise<string>;
/**
* Adds and loads a new JS or CSS files into the panel.
*/
addScript(handle: ViewHandle, scriptPath: string): Promise<void>;
/**
* Called when a message is sent from the webview (using postMessage).
*
* To post a message from the webview to the plugin use:
*
* ```javascript
* const response = await webviewApi.postMessage(message);
* ```
*
* - `message` can be any JavaScript object, string or number
* - `response` is whatever was returned by the `onMessage` handler
*
* Using this mechanism, you can have two-way communication between the
* plugin and webview.
*
* See the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages) for more details.
*
*/
onMessage(handle: ViewHandle, callback: Function): Promise<void>;
/**
* Sends a message to the webview.
*
* The webview must have registered a message handler prior, otherwise the message is ignored. Use;
*
* ```javascript
* webviewApi.onMessage((message) => { ... });
* ```
*
* - `message` can be any JavaScript object, string or number
*
* The view API may have only one onMessage handler defined.
* This method is fire and forget so no response is returned.
*
* It is particularly useful when the webview needs to react to events emitted by the plugin or the joplin api.
*/
postMessage(handle: ViewHandle, message: any): void;
/**
* Shows the panel
*/
show(handle: ViewHandle, show?: boolean): Promise<void>;
/**
* Hides the panel
*/
hide(handle: ViewHandle): Promise<void>;
/**
* Tells whether the panel is visible or not
*/
visible(handle: ViewHandle): Promise<boolean>;
}

View File

@@ -0,0 +1,16 @@
import { ToolbarButtonLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -0,0 +1,24 @@
import Plugin from '../Plugin';
export interface Implementation {
injectCustomStyles(elementId: string, cssFilePath: string): Promise<void>;
}
export default class JoplinWindow {
private plugin_;
private store_;
private implementation_;
constructor(implementation: Implementation, plugin: Plugin, store: any);
/**
* Loads a chrome CSS file. It will apply to the window UI elements, except
* for the note viewer. It is the same as the "Custom stylesheet for
* Joplin-wide app styles" setting. See the [Load CSS Demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/load_css)
* for an example.
*/
loadChromeCssFile(filePath: string): Promise<void>;
/**
* Loads a note CSS file. It will apply to the note viewer, as well as any
* exported or printed note. It is the same as the "Custom stylesheet for
* rendered Markdown" setting. See the [Load CSS Demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/load_css)
* for an example.
*/
loadNoteCssFile(filePath: string): Promise<void>;
}

View File

@@ -0,0 +1,87 @@
import { FolderEntity } from '../../database/types';
import { Disposable, MenuItem } from './types';
export interface EditContextMenuFilterObject {
items: MenuItem[];
}
declare type FilterHandler<T> = (object: T) => Promise<void>;
declare enum ItemChangeEventType {
Create = 1,
Update = 2,
Delete = 3
}
interface ItemChangeEvent {
id: string;
event: ItemChangeEventType;
}
interface SyncStartEvent {
withErrors: boolean;
}
interface ResourceChangeEvent {
id: string;
}
declare type ItemChangeHandler = (event: ItemChangeEvent) => void;
declare type SyncStartHandler = (event: SyncStartEvent) => void;
declare type ResourceChangeHandler = (event: ResourceChangeEvent) => void;
/**
* The workspace service provides access to all the parts of Joplin that
* are being worked on - i.e. the currently selected notes or notebooks as
* well as various related events, such as when a new note is selected, or
* when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;
constructor(store: any);
/**
* Called when a new note or notes are selected.
*/
onNoteSelectionChange(callback: Function): Promise<Disposable>;
/**
* Called when the content of a note changes.
* @deprecated Use `onNoteChange()` instead, which is reliably triggered whenever the note content, or any note property changes.
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**
* Called when a resource is changed. Currently this handled will not be
* called when a resource is added or deleted.
*/
onResourceChange(handler: ResourceChangeHandler): Promise<void>;
/**
* Called when an alarm associated with a to-do is triggered.
*/
onNoteAlarmTrigger(handler: Function): Promise<Disposable>;
/**
* Called when the synchronisation process is starting.
*/
onSyncStart(handler: SyncStartHandler): Promise<Disposable>;
/**
* Called when the synchronisation process has finished.
*/
onSyncComplete(callback: Function): Promise<Disposable>;
/**
* Called just before the editor context menu is about to open. Allows
* adding items to it.
*/
filterEditorContextMenu(handler: FilterHandler<EditContextMenuFilterObject>): void;
/**
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/
selectedNoteIds(): Promise<string[]>;
}
export {};

View File

@@ -0,0 +1,5 @@
import type Joplin from './Joplin';
declare const joplin: Joplin;
export default joplin;

View File

@@ -0,0 +1,674 @@
// =================================================================
// Command API types
// =================================================================
export interface Command {
/**
* Name of command - must be globally unique
*/
name: string;
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
* If it is missing, it's assumed it's a private command, to be called programmatically only.
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string;
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string;
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args: any[]): Promise<any | void>;
/**
* Defines whether the command should be enabled or disabled, which in turns
* affects the enabled state of any associated button or menu item.
*
* The condition should be expressed as a "when-clause" (as in Visual Studio
* Code). It's a simple boolean expression that evaluates to `true` or
* `false`. It supports the following operators:
*
* Operator | Symbol | Example
* -- | -- | --
* Equality | == | "editorType == markdown"
* Inequality | != | "currentScreen != config"
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Joplin, unlike VSCode, also supports parenthesis, which allows creating
* more complex expressions such as `cond1 || (cond2 && cond3)`. Only one
* level of parenthesis is possible (nested ones aren't supported).
*
* Currently the supported context variables aren't documented, but you can
* find the list below:
*
* - [Global When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts)
* - [Desktop app When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/app-desktop/services/commands/stateToWhenClauseContext.ts)
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string;
}
// =================================================================
// Interop API types
// =================================================================
export enum FileSystemItem {
File = 'file',
Directory = 'directory',
}
export enum ImportModuleOutputFormat {
Markdown = 'md',
Html = 'html',
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
* To get a better sense of what it will contain it can be useful to print it using `console.info(context)`.
*/
export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[];
/**
* Called when the export process starts.
*/
onInit(context: ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context: ExportContext): Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[];
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[];
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat;
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context: ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
// modulePath?: string;
target?: FileSystemItem;
}
export interface ExportContext {
destPath: string;
options: ExportOptions;
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any;
}
export interface ImportContext {
sourcePath: string;
options: any;
warnings: string[];
}
// =================================================================
// Misc types
// =================================================================
export interface Script {
onStart?(event: any): Promise<void>;
}
export interface Disposable {
// dispose():void;
}
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,
}
// =================================================================
// Menu types
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string;
}
export enum MenuItemLocation {
File = 'file',
Edit = 'edit',
View = 'view',
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
Context = 'context',
// If adding an item here, don't forget to update isContextMenuItemLocation()
/**
* When a command is called from the note list context menu, the
* command will receive the following arguments:
*
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
* When a command is called from a folder context menu, the
* command will receive the following arguments:
*
* - `folderId:string`: ID of the folder that was right-clicked on
*/
FolderContextMenu = 'folderContextMenu',
/**
* When a command is called from a tag context menu, the
* command will receive the following arguments:
*
* - `tagId:string`: ID of the tag that was right-clicked on
*/
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,
MenuItemLocation.EditorContextMenu,
MenuItemLocation.FolderContextMenu,
MenuItemLocation.TagContextMenu,
].includes(location);
}
export interface MenuItem {
/**
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string;
/**
* Arguments that should be passed to the command. They will be as rest
* parameters.
*/
commandArgs?: any[];
/**
* Set to "separator" to create a divider line
*/
type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio');
/**
* Accelerator associated with the menu item
*/
accelerator?: string;
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[];
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string;
}
// =================================================================
// View API types
// =================================================================
export interface ButtonSpec {
id: ButtonId;
title?: string;
onClick?(): void;
}
export type ButtonId = string;
export enum ToolbarButtonLocation {
/**
* This toolbar in the top right corner of the application. It applies to the note as a whole, including its metadata.
*/
NoteToolbar = 'noteToolbar',
/**
* This toolbar is right above the text editor. It applies to the note body only.
*/
EditorToolbar = 'editorToolbar',
}
export type ViewHandle = string;
export interface EditorCommand {
name: string;
value?: any;
}
export interface DialogResult {
id: ButtonId;
formData?: any;
}
// =================================================================
// Settings types
// =================================================================
export enum SettingItemType {
Int = 1,
String = 2,
Bool = 3,
Array = 4,
Object = 5,
Button = 6,
}
export enum SettingItemSubType {
FilePathAndArgs = 'file_path_and_args',
FilePath = 'file_path', // Not supported on mobile!
DirectoryPath = 'directory_path', // Not supported on mobile!
}
export enum AppType {
Desktop = 'desktop',
Mobile = 'mobile',
Cli = 'cli',
}
export enum SettingStorage {
Database = 1,
File = 2,
}
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any;
type: SettingItemType;
/**
* Currently only used to display a file or directory selector. Always set
* `type` to `SettingItemType.String` when using this property.
*/
subType?: SettingItemSubType;
label: string;
description?: string;
/**
* A public setting will appear in the Configuration screen and will be
* modifiable by the user. A private setting however will not appear there,
* and can only be changed programmatically. You may use this to store some
* values that you do not want to directly expose.
*/
public: boolean;
/**
* You would usually set this to a section you would have created
* specifically for the plugin.
*/
section?: string;
/**
* To create a setting with multiple options, set this property to `true`.
* That setting will render as a dropdown list in the configuration screen.
*/
isEnum?: boolean;
/**
* This property is required when `isEnum` is `true`. In which case, it
* should contain a map of value => label.
*/
options?: Record<any, any>;
/**
* Reserved property. Not used at the moment.
*/
appTypes?: AppType[];
/**
* Set this to `true` to store secure data, such as passwords. Any such
* setting will be stored in the system keychain if one is available.
*/
secure?: boolean;
/**
* An advanced setting will be moved under the "Advanced" button in the
* config screen.
*/
advanced?: boolean;
/**
* Set the min, max and step values if you want to restrict an int setting
* to a particular range.
*/
minimum?: number;
maximum?: number;
step?: number;
/**
* Either store the setting in the database or in settings.json. Defaults to database.
*/
storage?: SettingStorage;
}
export interface SettingSection {
label: string;
iconName?: string;
description?: string;
name?: string;
}
// =================================================================
// Data API types
// =================================================================
/**
* An array of at least one element and at most three elements.
*
* - **[0]**: Resource name (eg. "notes", "folders", "tags", etc.)
* - **[1]**: (Optional) Resource ID.
* - **[2]**: (Optional) Resource link.
*/
export type Path = string[];
// =================================================================
// Content Script types
// =================================================================
export type PostMessageHandler = (message: any)=> Promise<any>;
/**
* When a content script is initialised, it receives a `context` object.
*/
export interface ContentScriptContext {
/**
* The plugin ID that registered this content script
*/
pluginId: string;
/**
* The content script ID, which may be necessary to post messages
*/
contentScriptId: string;
/**
* Can be used by CodeMirror content scripts to post a message to the plugin
*/
postMessage: PostMessageHandler;
}
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow the template
* below.
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: function(markdownIt, options) {
* // ...
* },
* assets: {
* // ...
* },
* }
* }
* }
* ```
* See [the
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* for a simple Markdown-it plugin example.
*
* ## Exported members
*
* - The `context` argument is currently unused but could be used later on
* to provide access to your own plugin so that the content script and
* plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check
* the [official doc](https://github.com/markdown-it/markdown-it) for more
* information. The `options` parameter is of type
* [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml.ts),
* which contains a number of options, mostly useful for Joplin's internal
* code.
*
* - Using the **optional** `assets` key you may specify assets such as JS
* or CSS that should be loaded in the rendered HTML document. Check for
* example the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured.
*
* ## Posting messages from the content script to your plugin
*
* The application provides the following function to allow executing
* commands from the rendered HTML code:
*
* ```javascript
* const response = await webviewApi.postMessage(contentScriptId, message);
* ```
*
* - `contentScriptId` is the ID you've defined when you registered the
* content script. You can retrieve it from the
* {@link ContentScriptContext | context}.
* - `message` can be any basic JavaScript type (number, string, plain
* object), but it cannot be a function or class instance.
*
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
*
* ## Registering an existing Markdown-it plugin
*
* To include a regular Markdown-It plugin, that doesn't make use of any
* Joplin-specific features, you would simply create a file such as this:
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: require('markdown-it-toc-done-right');
* }
* }
* }
* ```
*/
MarkdownItPlugin = 'markdownItPlugin',
/**
* Registers a new CodeMirror plugin, which should follow the template
* below.
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: function(CodeMirror) {
* // ...
* },
* codeMirrorResources: [],
* codeMirrorOptions: {
* // ...
* },
* assets: {
* // ...
* },
* }
* }
* }
* ```
*
* - The `context` argument is currently unused but could be used later on
* to provide access to your own plugin so that the content script and
* plugin can communicate.
*
* - The `plugin` key is your CodeMirror plugin. This is where you can
* register new commands with CodeMirror or interact with the CodeMirror
* instance as needed.
*
* - The `codeMirrorResources` key is an array of CodeMirror resources that
* will be loaded and attached to the CodeMirror module. These are made up
* of addons, keymaps, and modes. For example, for a plugin that want's to
* enable clojure highlighting in code blocks. `codeMirrorResources` would
* be set to `['mode/clojure/clojure']`.
*
* - The `codeMirrorOptions` key contains all the
* [CodeMirror](https://codemirror.net/doc/manual.html#config) options
* that will be set or changed by this plugin. New options can alse be
* declared via
* [`CodeMirror.defineOption`](https://codemirror.net/doc/manual.html#defineOption),
* and then have their value set here. For example, a plugin that enables
* line numbers would set `codeMirrorOptions` to `{'lineNumbers': true}`.
*
* - Using the **optional** `assets` key you may specify **only** CSS assets
* that should be loaded in the rendered HTML document. Check for example
* the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured.
*
* One of the `plugin`, `codeMirrorResources`, or `codeMirrorOptions` keys
* must be provided for the plugin to be valid. Having multiple or all
* provided is also okay.
*
* See also the [demo
* plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
* for an example of all these keys being used in one plugin.
*
* ## Posting messages from the content script to your plugin
*
* In order to post messages to the plugin, you can use the postMessage
* function passed to the {@link ContentScriptContext | context}.
*
* ```javascript
* const response = await context.postMessage('messageFromCodeMirrorContentScript');
* ```
*
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
*
*/
CodeMirrorPlugin = 'codeMirrorPlugin',
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
{
"name": "joplin-plugin-userdata-demo",
"version": "1.0.0",
"scripts": {
"dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive",
"prepare": "npm run dist",
"update": "npm install -g generator-joplin && yo joplin --update"
},
"license": "MIT",
"keywords": [
"joplin-plugin"
],
"devDependencies": {
"@types/node": "^14.0.14",
"chalk": "^4.1.0",
"copy-webpack-plugin": "^6.1.0",
"fs-extra": "^9.0.1",
"glob": "^7.1.6",
"on-build-webpack": "^0.1.0",
"tar": "^6.0.5",
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"yargs": "^16.2.0"
},
"files": [
"publish"
]
}

View File

@@ -0,0 +1,3 @@
{
"extraScripts": []
}

View File

@@ -0,0 +1,17 @@
import joplin from 'api';
import { ModelType } from 'api/types';
joplin.plugins.register({
onStart: async function() {
const folder = await joplin.data.post(['folders'], null, { title: "test" });
const note = await joplin.data.post(['notes'], null, { title: "test", parent_id: folder.id });
await joplin.data.userDataSet(ModelType.Note, note.id, 'mykey', 'abcd');
console.info('Got back user data:', await joplin.data.userDataGet(ModelType.Note, note.id, 'mykey'));
await joplin.data.userDataDelete(ModelType.Note, note.id, 'mykey');
console.info('Got back user data:', await joplin.data.userDataGet(ModelType.Note, note.id, 'mykey'));
},
});

View File

@@ -0,0 +1,12 @@
{
"manifest_version": 1,
"id": "org.joplinapp.plugins.UserDataDemo",
"app_min_version": "2.0",
"version": "1.0.0",
"name": "User Data Demo",
"description": "",
"author": "",
"homepage_url": "",
"repository_url": "",
"keywords": []
}

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "./dist/",
"module": "commonjs",
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -0,0 +1,291 @@
// -----------------------------------------------------------------------------
// This file is used to build the plugin file (.jpl) and plugin info (.json). It
// is recommended not to edit this file as it would be overwritten when updating
// the plugin framework. If you do make some changes, consider using an external
// JS file and requiring it here to minimize the changes. That way when you
// update, you can easily restore the functionality you've added.
// -----------------------------------------------------------------------------
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const userConfigFilename = './plugin.config.json';
const userConfigPath = path.resolve(rootDir, userConfigFilename);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const userConfig = Object.assign({}, {
extraScripts: [],
}, fs.pathExistsSync(userConfigPath) ? require(userConfigFilename) : {});
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const allPossibleCategories = ['appearance', 'developer tools', 'productivity', 'themes', 'integrations', 'viewer', 'search', 'tags', 'editor', 'files', 'personal knowledge management'];
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function validateCategories(categories) {
if (!categories) return null;
if ((categories.length !== new Set(categories).size)) throw new Error('Repeated categories are not allowed');
categories.forEach(category => {
if (!allPossibleCategories.includes(category)) throw new Error(`${category} is not a valid category. Please make sure that the category name is lowercase. Valid Categories are: \n${allPossibleCategories}\n`);
});
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
const output = JSON.parse(content);
if (!output.id) throw new Error(`Manifest plugin ID is not set in ${manifestPath}`);
validateCategories(output.categories);
return output;
}
function createPluginArchive(sourceDir, destPath) {
const distFiles = glob.sync(`${sourceDir}/**/*`, { nodir: true })
.map(f => f.substr(sourceDir.length + 1));
if (!distFiles.length) throw new Error('Plugin archive was not created because the "dist" directory is empty');
fs.removeSync(destPath);
tar.create(
{
strict: true,
portable: true,
file: destPath,
cwd: sourceDir,
sync: true,
},
distFiles
);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
function onBuildCompleted() {
try {
fs.removeSync(path.resolve(publishDir, 'index.js'));
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
} catch (error) {
console.error(chalk.red(error.message));
}
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
};
const pluginConfig = Object.assign({}, baseConfig, {
entry: './src/index.ts',
resolve: {
alias: {
api: path.resolve(__dirname, 'api'),
},
// JSON files can also be required from scripts so we include this.
// https://github.com/joplin/plugin-bibtex/pull/2
extensions: ['.js', '.tsx', '.ts', '.json'],
},
output: {
filename: 'index.js',
path: distDir,
},
plugins: [
new CopyPlugin({
patterns: [
{
from: '**/*',
context: path.resolve(__dirname, 'src'),
to: path.resolve(__dirname, 'dist'),
globOptions: {
ignore: [
// All TypeScript files are compiled to JS and
// already copied into /dist so we don't copy them.
'**/*.ts',
'**/*.tsx',
],
},
},
],
}),
],
});
const extraScriptConfig = Object.assign({}, baseConfig, {
resolve: {
alias: {
api: path.resolve(__dirname, 'api'),
},
extensions: ['.js', '.tsx', '.ts', '.json'],
},
});
const createArchiveConfig = {
stats: 'errors-only',
entry: './dist/index.js',
output: {
filename: 'index.js',
path: publishDir,
},
plugins: [new WebpackOnBuildPlugin(onBuildCompleted)],
};
function resolveExtraScriptPath(name) {
const relativePath = `./src/${name}`;
const fullPath = path.resolve(`${rootDir}/${relativePath}`);
if (!fs.pathExistsSync(fullPath)) throw new Error(`Could not find extra script: "${name}" at "${fullPath}"`);
const s = name.split('.');
s.pop();
const nameNoExt = s.join('.');
return {
entry: relativePath,
output: {
filename: `${nameNoExt}.js`,
path: distDir,
library: 'default',
libraryTarget: 'commonjs',
libraryExport: 'default',
},
};
}
function buildExtraScriptConfigs(userConfig) {
if (!userConfig.extraScripts.length) return [];
const output = [];
for (const scriptName of userConfig.extraScripts) {
const scriptPaths = resolveExtraScriptPath(scriptName);
output.push(Object.assign({}, extraScriptConfig, {
entry: scriptPaths.entry,
output: scriptPaths.output,
}));
}
return output;
}
function main(processArgv) {
const yargs = require('yargs/yargs');
const argv = yargs(processArgv).argv;
const configName = argv['joplin-plugin-config'];
if (!configName) throw new Error('A config file must be specified via the --joplin-plugin-config flag');
// Webpack configurations run in parallel, while we need them to run in
// sequence, and to do that it seems the only way is to run webpack multiple
// times, with different config each time.
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
// overwritten here by the compiled version. This is by design. The
// result is that JS files that don't need compilation, are simply
// copied to /dist, while those that do need it are correctly compiled.
buildExtraScripts: buildExtraScriptConfigs(userConfig),
// Ths config is for creating the .jpl, which is done via the plugin, so
// it doesn't actually need an entry and output, however webpack won't
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
};
// If we are running the first config step, we clean up and create the build
// directories.
if (configName === 'buildMain') {
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
}
return configs[configName];
}
let exportedConfigs = [];
try {
exportedConfigs = main(process.argv);
} catch (error) {
console.error(chalk.red(error.message));
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -25,6 +25,7 @@ import { themeStyle } from '@joplin/lib/theme';
import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
import type { Editor } from 'tinymce';
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -557,7 +558,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
'bullist', 'numlist', 'joplinChecklist', '|',
'h1', 'h2', 'h3', 'hr', 'blockquote', 'table', `joplinInsertDateTime${toolbarPluginButtons}`,
'h1', 'h2', 'h3', 'hr', 'blockquote', 'inserttable', `joplinInsertDateTime${toolbarPluginButtons}`,
];
// Available table toolbar buttons:
// https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#tableplugin
const tableToolbar = [
'tabledelete',
'tableinsertrowafter tablecopyrow tablepasterowafter tabledeleterow',
'tableinsertcolafter tablecopycol tablepastecolafter tabledeletecol',
];
const editors = await (window as any).tinymce.init({
@@ -576,6 +585,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
branding: false,
statusbar: false,
target_list: false,
// Handle the first table row as table header.
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
table_header_type: 'sectionCells',
table_toolbar: tableToolbar.join(' | '),
table_resize_bars: false,
language_url: ['en_US', 'en_GB'].includes(language) ? undefined : `${bridge().vendorDir()}/lib/tinymce/langs/${language}`,
toolbar: toolbar.join(' '),
@@ -589,7 +602,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
joplinSub: { inline: 'sub', remove: 'all' },
joplinSup: { inline: 'sup', remove: 'all' },
},
setup: (editor: any) => {
setup: (editor: Editor) => {
editor.ui.registry.addButton('joplinAttach', {
tooltip: _('Attach file'),
icon: 'paperclip',
@@ -614,7 +627,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
onAction: function() {
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' });
},
onSetup: function(api: any) {
onSetup: function(api) {
api.setActive(editor.formatter.match('code'));
const unbind = editor.formatter.formatChanged('code', api.setActive).unbind;
@@ -624,6 +637,22 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
},
});
editor.ui.registry.addMenuButton('inserttable', {
icon: 'table',
tooltip: 'Table',
fetch: (callback) => {
callback([
{
type: 'fancymenuitem',
fancytype: 'inserttable',
onAction: (data) => {
editor.execCommand('mceInsertTable', false, { rows: data.numRows, columns: data.numColumns, options: { headerRows: 1 } });
},
},
]);
},
});
editor.ui.registry.addButton('joplinInsertDateTime', {
tooltip: _('Insert time'),
icon: 'insert-time',
@@ -647,13 +676,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
editor.addShortcut('Meta+Shift+9', '', () => editor.execCommand('InsertJoplinChecklist'));
// TODO: remove event on unmount?
editor.on('DblClick', (event: any) => {
editor.on('DblClick', (event) => {
const editable = findEditableContainer(event.target);
if (editable) openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable);
});
// This is triggered when an external file is dropped on the editor
editor.on('drop', (event: any) => {
editor.on('drop', (event) => {
// Prevent the message "Dropped file type is not
// supported" to show up. It was added in a recent
// TinyMCE version and doesn't apply since we do support
@@ -664,7 +693,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
props_onDrop.current(event);
});
editor.on('ObjectResized', (event: any) => {
editor.on('ObjectResized', (event) => {
if (event.target.nodeName === 'IMG') {
editor.fire(TinyMceEditorEvents.JoplinChange);
dispatchDidUpdate(editor);

View File

@@ -100,9 +100,8 @@ export const StyledExpandLink = styled.a`
`;
export const StyledNoteCount = styled.div`
color: ${(props: any) => props.theme.color2};
color: ${(props: any) => props.theme.colorFaded2};
padding-left: 8px;
opacity: 0.5;
user-select: none;
`;

View File

@@ -148,7 +148,7 @@
"electron-window-state": "5.0.3",
"formatcoords": "1.1.3",
"fs-extra": "11.1.1",
"highlight.js": "11.7.0",
"highlight.js": "11.8.0",
"immer": "7.0.15",
"keytar": "7.9.0",
"mark.js": "8.11.1",

View File

@@ -129,7 +129,10 @@ export default class PluginRunner extends BasePluginRunner {
if (plugin.devMode) {
pluginWindow.webContents.once('dom-ready', () => {
pluginWindow.webContents.openDevTools({ mode: 'detach' });
// Need to open with a delay, otherwise it doesn't show up
setTimeout(() => {
pluginWindow.webContents.openDevTools({ mode: 'detach' });
}, 3000);
});
}

View File

@@ -4,5 +4,15 @@
# It could be used to develop plugins too.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PLUGIN_PATH=~/src/joplin-rich-markdown
npm install --prefix="$PLUGIN_PATH" && yarn start --dev-plugins "$PLUGIN_PATH"
TEMP_PATH=~/src/plugin-tests
PLUGIN_PATH=~/src/joplin/packages/app-cli/tests/support/plugins/user_data
mkdir -p "$TEMP_PATH"
PLUGIN_NAME=$(echo "$PLUGIN_PATH" | awk -F/ '{print $NF}')
TEMP_PLUGIN_PATH="$TEMP_PATH/$PLUGIN_NAME"
rsync -a --delete "$PLUGIN_PATH/" "$TEMP_PLUGIN_PATH/"
npm install --prefix="$TEMP_PLUGIN_PATH" && yarn start --dev-plugins "$TEMP_PLUGIN_PATH"
# Add eg "--profile $HOME/.config/joplindev-desktop-1" to test with a different profile

View File

@@ -1,6 +0,0 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View File

@@ -1,73 +0,0 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
node_modules/warning/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
[include]
[libs]
node_modules/react-native/interface.js
node_modules/react-native/flow/
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.file_ext=.js
module.file_ext=.json
module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
[version]
^0.122.0

View File

@@ -31,6 +31,8 @@ local.properties
*.iml
*.hprof
.cxx/
*.keystore
!debug.keystore
# node.js
#
@@ -38,12 +40,6 @@ node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
@@ -63,6 +59,9 @@ buck-out/
/ios/Pods/
/vendor/bundle/
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
# Custom
lib/csstojs/
lib/rnInjectedJs/

View File

@@ -0,0 +1 @@
18

View File

@@ -1 +0,0 @@
2.7.5

View File

@@ -1,4 +1,4 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.5'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
ruby '>= 2.6.10'
gem 'cocoapods', '>= 1.11.3'

View File

@@ -1,119 +1,77 @@
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
/* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/
project.ext.react = [
// 2023-05-09: This seems to be optional, but it's not. Without it, the app
// will crash on certain devices with this error:
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
// codegenDir = file("../node_modules/react-native-codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
/* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// > java.lang.UnsatisfiedLinkError: couldn't find DSO to load: libhermes.so"
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// https://github.com/laurent22/joplin/issues/8144#issuecomment-1539629812
enableHermes: true, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
}
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
* Set this to true to create four separate APKs instead of one,
* one for each native architecture. This is useful if you don't
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
@@ -122,16 +80,9 @@ def enableProguardInReleaseBuilds = false
def jscFlavor = 'org.webkit:android-jsc-intl:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
* Private function to get the list of Native Architectures you want to build.
* This reads the value from reactNativeArchitectures in your gradle.properties
* file and works together with the --active-arch-only flag of react-native run-android.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
@@ -139,6 +90,8 @@ def reactNativeArchitectures() {
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
@@ -146,18 +99,19 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
dexOptions {
// To fix "GC overhead limit exceeded"
// https://stackoverflow.com/q/32133013/561309
javaMaxHeapSize "4g"
}
// dexOptions {
// // To fix "GC overhead limit exceeded"
// // https://stackoverflow.com/q/32133013/561309
// javaMaxHeapSize "4g"
// }
namespace "net.cozic.joplin"
defaultConfig {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097711
versionName "2.11.26"
versionCode 2097712
versionName "2.11.27"
// ndk {
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
// }
@@ -166,67 +120,7 @@ android {
missingDimensionStrategy 'react-native-camera', 'general'
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
multiDexEnabled true
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the CMake build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
arguments "-DPROJECT_BUILD_DIR=$buildDir",
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
"-DANDROID_STL=c++_shared"
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
path "$projectDir/src/main/jni/CMakeLists.txt"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureCMakeDebug* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
configureCMakeDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
multiDexEnabled true
}
splits {
@@ -288,10 +182,10 @@ android {
// > Execution failed for task ':app:lintVitalRelease'
//
// https://stackoverflow.com/a/62603296/561309
lintOptions {
disable 'InvalidPackage'
checkReleaseBuilds false
}
// lintOptions {
// disable 'InvalidPackage'
// checkReleaseBuilds false
// }
}
dependencies {
@@ -301,72 +195,28 @@ dependencies {
exclude group: 'com.google.android.gms', module: 'play-services-vision'
}
implementation fileTree(dir: "libs", include: ["*.jar"])
// implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
// implementation "com.facebook.react:react-native:+" // From node_modules
implementation ("com.facebook.react:react-native") version {
strictly "0.70.6" // pass in your react-native version
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
implementation 'com.android.support:multidex:2.0.1'
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
// implementation 'com.android.support:multidex:2.0.1'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

View File

@@ -1,19 +0,0 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

View File

@@ -17,7 +17,6 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
@@ -25,13 +24,16 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
/**
* Class responsible of loading Flipper inside your React Native application. This is the debug
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());

View File

@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.cozic.joplin"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
@@ -8,6 +7,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATION" />
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->

View File

@@ -2,7 +2,8 @@ package net.cozic.joplin;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
public class MainActivity extends ReactActivity {
@@ -16,31 +17,19 @@ public class MainActivity extends ReactActivity {
}
/**
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
* (Paper).
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
* (aka React 18) with two boolean flags.
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new MainActivityDelegate(this, getMainComponentName());
}
public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
@Override
protected ReactRootView createRootView() {
ReactRootView reactRootView = new ReactRootView(getContext());
return new DefaultReactActivityDelegate(
this,
getMainComponentName(),
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
return reactRootView;
}
@Override
protected boolean isConcurrentRootEnabled() {
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
);
}
}

View File

@@ -1,41 +1,38 @@
package net.cozic.joplin;
import android.app.Application;
import android.content.Context;
import android.database.CursorWindow;
import android.webkit.WebView;
import androidx.multidex.MultiDex;
// import androidx.multidex.MultiDex;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.oblador.vectoricons.VectorIconsPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import net.cozic.joplin.newarchitecture.MainApplicationReactNativeHost;
import net.cozic.joplin.share.SharePackage;
import net.cozic.joplin.ssl.SslPackage;
import net.cozic.joplin.textinput.TextInputPackage;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
// @Override
// protected void attachBaseContext(Context base) {
// super.attachBaseContext(base);
// MultiDex.install(this);
// }
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
new DefaultReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
@@ -56,27 +53,26 @@ public class MainApplication extends Application implements ReactApplication {
protected String getJSMainModuleName() {
return "index";
}
};
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@Override
protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
// To try to fix the error "Row too big to fit into CursorWindow"
// https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153
// https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991
@@ -89,46 +85,10 @@ public class MainApplication extends Application implements ReactApplication {
}
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
// To allow debugging the webview using the Chrome developer tools.
// Open chrome://inspect/#devices to view the device and connect to it
// IMPORTANT: USB debugging must be enabled on the device for it to work.
// https://github.com/react-native-webview/react-native-webview/blob/master/docs/Debugging.md
if (BuildConfig.DEBUG) {
WebView.setWebContentsDebuggingEnabled(true);
}
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("net.cozic.joplin.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
}

View File

@@ -1,117 +0,0 @@
package net.cozic.joplin.newarchitecture;
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.EmptyReactNativeConfig;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.fabric.ReactNativeConfig;
import com.facebook.react.uimanager.ViewManagerRegistry;
import net.cozic.joplin.BuildConfig;
import net.cozic.joplin.newarchitecture.components.MainComponentsRegistry;
import net.cozic.joplin.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
* TurboModule delegates and the Fabric Renderer.
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationReactNativeHost extends ReactNativeHost {
public MainApplicationReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
// packages.add(new TurboReactPackage() { ... });
// If you have custom Fabric Components, their ViewManagers should also be loaded here
// inside a ReactPackage.
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@NonNull
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
// for the new architecture and to use TurboModules correctly.
return new MainApplicationTurboModuleManagerDelegate.Builder();
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
// Here we provide a new JSIModuleSpec that will be responsible of providing the
// custom Fabric Components.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Here we register a Components Registry.
// The one that is generated with the template contains no components
// and just provides you the one from React Native core.
MainComponentsRegistry.register(componentFactory);
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry);
}
});
return specs;
}
};
}
}

View File

@@ -1,36 +0,0 @@
package net.cozic.joplin.newarchitecture.components;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
/**
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
@DoNotStrip
public class MainComponentsRegistry {
static {
SoLoader.loadLibrary("fabricjni");
}
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MainComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory);
}
@DoNotStrip
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
return new MainComponentsRegistry(componentFactory);
}
}

View File

@@ -1,48 +0,0 @@
package net.cozic.joplin.newarchitecture.modules;
import com.facebook.jni.HybridData;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.soloader.SoLoader;
import java.util.List;
/**
* Class responsible to load the TurboModules. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationTurboModuleManagerDelegate
extends ReactPackageTurboModuleManagerDelegate {
private static volatile boolean sIsSoLibraryLoaded;
protected MainApplicationTurboModuleManagerDelegate(
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
super(reactApplicationContext, packages);
}
protected native HybridData initHybrid();
native boolean canCreateTurboModule(String moduleName);
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
protected MainApplicationTurboModuleManagerDelegate build(
ReactApplicationContext context, List<ReactPackage> packages) {
return new MainApplicationTurboModuleManagerDelegate(context, packages);
}
}
@Override
protected synchronized void maybeLoadOtherSoLibraries() {
if (!sIsSoLibraryLoaded) {
// If you change the name of your application .so file in the Android.mk file,
// make sure you update the name here as well.
SoLoader.loadLibrary("joplin_appmodules");
sIsSoLibraryLoaded = true;
}
}
}

View File

@@ -1,5 +0,0 @@
cmake_minimum_required(VERSION 3.13)
# Define the library name here.
project(joplin_appmodules)
# This file includes all the necessary to let you build your application with the New Architecture.
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)

View File

@@ -1,31 +0,0 @@
#include "MainApplicationModuleProvider.h"
#include <rncore.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string &moduleName,
const JavaTurboModule::InitParams &params) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`:
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
// Module providers autolinked by RN CLI
auto rncli_module = rncli_ModuleProvider(moduleName, params);
if (rncli_module != nullptr) {
return rncli_module;
}
return rncore_ModuleProvider(moduleName, params);
}
} // namespace react
} // namespace facebook

View File

@@ -1,16 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <ReactCommon/JavaTurboModule.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string &moduleName,
const JavaTurboModule::InitParams &params);
} // namespace react
} // namespace facebook

View File

@@ -1,45 +0,0 @@
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainApplicationModuleProvider.h"
namespace facebook {
namespace react {
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
MainApplicationTurboModuleManagerDelegate::initHybrid(
jni::alias_ref<jhybridobject>) {
return makeCxxInstance();
}
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
makeNativeMethod(
"canCreateTurboModule",
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
});
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) {
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string &name,
const JavaTurboModule::InitParams &params) {
return MainApplicationModuleProvider(name, params);
}
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
const std::string &name) {
return getTurboModule(name, nullptr) != nullptr ||
getTurboModule(name, {.moduleName = name}) != nullptr;
}
} // namespace react
} // namespace facebook

View File

@@ -1,38 +0,0 @@
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook {
namespace react {
class MainApplicationTurboModuleManagerDelegate
: public jni::HybridClass<
MainApplicationTurboModuleManagerDelegate,
TurboModuleManagerDelegate> {
public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor =
"Lnet/cozic/joplin/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(
const std::string &name,
const JavaTurboModule::InitParams &params) override;
/**
* Test-only method. Allows user to verify whether a TurboModule can be
* created by instances of this class.
*/
bool canCreateTurboModule(const std::string &name);
};
} // namespace react
} // namespace facebook

View File

@@ -1,65 +0,0 @@
#include "MainComponentsRegistry.h"
#include <CoreComponentsRegistry.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>
#include <rncli.h>
namespace facebook {
namespace react {
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
std::shared_ptr<ComponentDescriptorProviderRegistry const>
MainComponentsRegistry::sharedProviderRegistry() {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
// Autolinked providers registered by RN CLI
rncli_registerProviders(providerRegistry);
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
return providerRegistry;
}
jni::local_ref<MainComponentsRegistry::jhybriddata>
MainComponentsRegistry::initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate) {
auto instance = makeCxxInstance(delegate);
auto buildRegistryFunction =
[](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto registry = MainComponentsRegistry::sharedProviderRegistry()
->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
auto mutableRegistry =
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{
eventDispatcher, contextContainer, nullptr}));
return registry;
};
delegate->buildRegistryFunction = buildRegistryFunction;
return instance;
}
void MainComponentsRegistry::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
});
}
} // namespace react
} // namespace facebook

View File

@@ -1,32 +0,0 @@
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MainComponentsRegistry
: public facebook::jni::HybridClass<MainComponentsRegistry> {
public:
// Adapt it to the package you used for your Java class.
constexpr static auto kJavaDescriptor =
"Lnet/cozic/joplin/newarchitecture/components/MainComponentsRegistry;";
static void registerNatives();
MainComponentsRegistry(ComponentFactory *delegate);
private:
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate);
};
} // namespace react
} // namespace facebook

View File

@@ -1,11 +0,0 @@
#include <fbjni/fbjni.h>
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainComponentsRegistry.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::MainApplicationTurboModuleManagerDelegate::
registerNatives();
facebook::react::MainComponentsRegistry::registerNatives();
});
}

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package net.cozic.joplin;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
/**
* Class responsible of loading Flipper inside your React Native application. This is the release
* flavor of it so it's empty as we don't want to load Flipper.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
// Do nothing as we don't want to initialize Flipper on Release.
}
}

View File

@@ -2,55 +2,55 @@
buildscript {
ext {
buildToolsVersion = "31.0.0"
buildToolsVersion = "33.0.0"
minSdkVersion = 21
compileSdkVersion = 31
targetSdkVersion = 31
compileSdkVersion = 33
targetSdkVersion = 33
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.2.1")
classpath("com.android.tools.build:gradle:7.3.1")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:5.0.1")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
jcenter()
// Seems to be required for react-native-vosk, otherwise the lib looks for it at "https://maven.aliyun.com/repository/jcenter/com/alphacephei/vosk-android/0.3.46/vosk-android-0.3.46.aar" but it's not there. And we get this error:
//
// Execution failed for task ':app:checkDebugAarMetadata'.
// > Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
// > Failed to transform vosk-android-0.3.46.aar (com.alphacephei:vosk-android:0.3.46) to match attributes {artifactType=android-aar-metadata, org.gradle.status=release}.
// > Could not find vosk-android-0.3.46.aar (com.alphacephei:vosk-android:0.3.46).
// Searched in the following locations:
// https://maven.aliyun.com/repository/jcenter/com/alphacephei/vosk-android/0.3.46/vosk-android-0.3.46.aar
//
// But according to this page, the lib is on the Apache repository:
//
// https://search.maven.org/artifact/com.alphacephei/vosk-android/0.3.46/aar
maven { url "https://maven.apache.org" }
maven {
// Required by react-native-fingerprint-scanner
// https://github.com/hieuvp/react-native-fingerprint-scanner/issues/192
url "https://maven.aliyun.com/repository/jcenter"
}
// Also required for react-native-vosk?
maven { url "https://maven.google.com" }
// Maybe still needed to fetch above package?
google()
maven { url 'https://www.jitpack.io' }
}

View File

@@ -39,6 +39,10 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
# To fix this error:
#
# > Failed to transform bcprov-jdk15on-1.68.jar

View File

@@ -3,10 +3,4 @@ include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
}
includeBuild('../node_modules/react-native-gradle-plugin')

View File

@@ -122,6 +122,11 @@ const ExtendedWebView = (props: Props, ref: Ref<WebViewControl>) => {
// - `setSupportMultipleWindows` must be `true` for security reasons:
// https://github.com/react-native-webview/react-native-webview/releases/tag/v11.0.0
// 2023-06-10: When the source is falsy, we set it to `{ uri: undefined }`
// to avoid various crashes and errors:
// https://github.com/react-native-webview/react-native-webview/issues/2920
// https://github.com/react-native-webview/react-native-webview/issues/2995
return (
<WebView
style={{
@@ -131,7 +136,7 @@ const ExtendedWebView = (props: Props, ref: Ref<WebViewControl>) => {
ref={webviewRef}
scrollEnabled={props.scrollEnabled}
useWebKit={true}
source={source}
source={source ? source : { uri: undefined }}
setSupportMultipleWindows={true}
hideKeyboardAccessoryView={true}
allowingReadAccessToURL={`file://${Setting.value('resourceDir')}`}

View File

@@ -694,12 +694,18 @@ class NoteScreenComponent extends BaseScreenComponent {
const newNote = { ...this.state.note };
if (this.state.mode === 'edit' && !!this.selection) {
const newText = `\n${resourceTag}\n`;
if (this.state.mode === 'edit') {
let newText = '';
const prefix = newNote.body.substring(0, this.selection.start);
const suffix = newNote.body.substring(this.selection.end);
newNote.body = `${prefix}${newText}${suffix}`;
if (this.selection) {
newText = `\n${resourceTag}\n`;
const prefix = newNote.body.substring(0, this.selection.start);
const suffix = newNote.body.substring(this.selection.end);
newNote.body = `${prefix}${newText}${suffix}`;
} else {
newText = `\n${resourceTag}`;
newNote.body = `${newNote.body}\n${newText}`;
}
if (this.useEditorBeta()) {
// The beta editor needs to be explicitly informed of changes
@@ -787,6 +793,12 @@ class NoteScreenComponent extends BaseScreenComponent {
}
public async onAlarmDialogAccept(date: Date) {
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
if (response !== PermissionsAndroid.RESULTS.GRANTED) {
logger.warn('POST_NOTIFICATION permission was not granted');
return;
}
const newNote = { ...this.state.note };
newNote.todo_due = date ? date.getTime() : 0;

View File

@@ -15,30 +15,39 @@ enum RecorderState {
Loading = 1,
Recording = 2,
Processing = 3,
Error = 4,
}
const useVosk = (): Vosk|null => {
const useVosk = (): [Error | null, Vosk|null] => {
const [vosk, setVosk] = useState<Vosk>(null);
const [error, setError] = useState<Error>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
const v = await getVosk();
if (event.cancelled) return;
setVosk(v);
try {
const v = await getVosk();
if (event.cancelled) return;
setVosk(v);
} catch (error) {
setError(error);
}
}, []);
return vosk;
return [error, vosk];
};
export default (props: Props) => {
const [recorder, setRecorder] = useState<Recorder>(null);
const [recorderState, setRecorderState] = useState<RecorderState>(RecorderState.Loading);
const vosk = useVosk();
const [voskError, vosk] = useVosk();
useEffect(() => {
if (!vosk) return;
setRecorderState(RecorderState.Recording);
}, [vosk]);
if (voskError) {
setRecorderState(RecorderState.Error);
} else if (vosk) {
setRecorderState(RecorderState.Recording);
}
}, [vosk, voskError]);
useEffect(() => {
if (recorderState === RecorderState.Recording) {
@@ -56,13 +65,14 @@ export default (props: Props) => {
}, [recorder, props.onDismiss]);
const renderContent = () => {
const components: Record<RecorderState, string> = {
[RecorderState.Loading]: _('Loading...'),
[RecorderState.Recording]: _('Please record your voice...'),
[RecorderState.Processing]: _('Converting speech to text...'),
const components: Record<RecorderState, Function> = {
[RecorderState.Loading]: () => _('Loading...'),
[RecorderState.Recording]: () => _('Please record your voice...'),
[RecorderState.Processing]: () => _('Converting speech to text...'),
[RecorderState.Error]: () => _('Error: %s', voskError.message),
};
return components[recorderState];
return components[recorderState]();
};
const renderIcon = () => {
@@ -70,6 +80,7 @@ export default (props: Props) => {
[RecorderState.Loading]: ({ size }: { size: number }) => <ActivityIndicator animating={true} style={{ width: size, height: size }} />,
[RecorderState.Recording]: 'microphone',
[RecorderState.Processing]: 'microphone',
[RecorderState.Error]: 'alert-circle-outline',
};
return components[recorderState];

View File

@@ -384,12 +384,14 @@
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -596,7 +598,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -659,7 +661,7 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@@ -1,9 +1,7 @@
#import <React/RCTBridgeDelegate.h>
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
@property (nonatomic, strong) UIWindow *window;
@interface AppDelegate : RCTAppDelegate
@end

View File

@@ -1,36 +1,12 @@
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
#import <React/RCTLinkingManager.h>
#import <RNCPushNotificationIOS.h>
#import "RNQuickActionManager.h"
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
// ===================================================
@@ -91,58 +67,17 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"Joplin", initProps);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
self.moduleName = @"Joplin";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
// BEGIN react-native-push-notification-ios
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
// END react-native-push-notification-ios
return YES;
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
@@ -154,43 +89,14 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
#endif
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
return true;
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end

View File

@@ -9,8 +9,23 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
#
# 2021-12-17: Changed back to 11.0 because after the fix it works with at least
# 12.x, and probably 11.0 too, which is the version supported by React Native.
platform :ios, '12.4'
install! 'cocoapods', :deterministic_uuids => false
platform :ios, min_ios_version_supported
prepare_react_native_project!
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
#
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
# ```js
# module.exports = {
# dependencies: {
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
# ```
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
end
target 'Joplin' do
config = use_native_modules!
@@ -26,13 +41,13 @@ target 'Joplin' do
# 2023/05/07: Leave that to `false` for now because Hermes is rubbish at
# reporting errors, which it makes it impossible to investigate crashes.
:hermes_enabled => false,
:hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled],
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable the next line.
:flipper_configuration => FlipperConfiguration.enabled,
:flipper_configuration => flipper_config,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)

View File

@@ -2,14 +2,14 @@ PODS:
- boost (1.76.0)
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- FBLazyVector (0.70.6)
- FBReactNativeSpec (0.70.6):
- FBLazyVector (0.71.10)
- FBReactNativeSpec (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.70.6)
- RCTTypeSafety (= 0.70.6)
- React-Core (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- RCTRequired (= 0.71.10)
- RCTTypeSafety (= 0.71.10)
- React-Core (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- Flipper (0.125.0):
- Flipper-Folly (~> 2.6)
- Flipper-RSocket (~> 1.4)
@@ -73,6 +73,9 @@ PODS:
- FlipperKit/FlipperKitNetworkPlugin
- fmt (6.2.1)
- glog (0.3.5)
- hermes-engine (0.71.10):
- hermes-engine/Pre-built (= 0.71.10)
- hermes-engine/Pre-built (0.71.10)
- JoplinCommonShareExtension (1.0.0)
- JoplinRNShareExtension (1.0.0):
- JoplinCommonShareExtension
@@ -90,203 +93,245 @@ PODS:
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCTRequired (0.70.6)
- RCTTypeSafety (0.70.6):
- FBLazyVector (= 0.70.6)
- RCTRequired (= 0.70.6)
- React-Core (= 0.70.6)
- React (0.70.6):
- React-Core (= 0.70.6)
- React-Core/DevSupport (= 0.70.6)
- React-Core/RCTWebSocket (= 0.70.6)
- React-RCTActionSheet (= 0.70.6)
- React-RCTAnimation (= 0.70.6)
- React-RCTBlob (= 0.70.6)
- React-RCTImage (= 0.70.6)
- React-RCTLinking (= 0.70.6)
- React-RCTNetwork (= 0.70.6)
- React-RCTSettings (= 0.70.6)
- React-RCTText (= 0.70.6)
- React-RCTVibration (= 0.70.6)
- React-bridging (0.70.6):
- RCT-Folly (= 2021.07.22.00)
- React-jsi (= 0.70.6)
- React-callinvoker (0.70.6)
- React-Codegen (0.70.6):
- FBReactNativeSpec (= 0.70.6)
- RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.70.6)
- RCTTypeSafety (= 0.70.6)
- React-Core (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-Core (0.70.6):
- RCT-Folly/Futures (2021.07.22.00):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- libevent
- RCTRequired (0.71.10)
- RCTTypeSafety (0.71.10):
- FBLazyVector (= 0.71.10)
- RCTRequired (= 0.71.10)
- React-Core (= 0.71.10)
- React (0.71.10):
- React-Core (= 0.71.10)
- React-Core/DevSupport (= 0.71.10)
- React-Core/RCTWebSocket (= 0.71.10)
- React-RCTActionSheet (= 0.71.10)
- React-RCTAnimation (= 0.71.10)
- React-RCTBlob (= 0.71.10)
- React-RCTImage (= 0.71.10)
- React-RCTLinking (= 0.71.10)
- React-RCTNetwork (= 0.71.10)
- React-RCTSettings (= 0.71.10)
- React-RCTText (= 0.71.10)
- React-RCTVibration (= 0.71.10)
- React-callinvoker (0.71.10)
- React-Codegen (0.71.10):
- FBReactNativeSpec
- hermes-engine
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- React-jsi
- React-jsiexecutor
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.6)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-Core/Default (= 0.71.10)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/CoreModulesHeaders (0.70.6):
- React-Core/CoreModulesHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/Default (0.70.6):
- React-Core/Default (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/DevSupport (0.70.6):
- React-Core/DevSupport (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.6)
- React-Core/RCTWebSocket (= 0.70.6)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-jsinspector (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-Core/Default (= 0.71.10)
- React-Core/RCTWebSocket (= 0.71.10)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-jsinspector (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTActionSheetHeaders (0.70.6):
- React-Core/RCTActionSheetHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTAnimationHeaders (0.70.6):
- React-Core/RCTAnimationHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTBlobHeaders (0.70.6):
- React-Core/RCTBlobHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTImageHeaders (0.70.6):
- React-Core/RCTImageHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTLinkingHeaders (0.70.6):
- React-Core/RCTLinkingHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTNetworkHeaders (0.70.6):
- React-Core/RCTNetworkHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTSettingsHeaders (0.70.6):
- React-Core/RCTSettingsHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTTextHeaders (0.70.6):
- React-Core/RCTTextHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTVibrationHeaders (0.70.6):
- React-Core/RCTVibrationHeaders (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-Core/RCTWebSocket (0.70.6):
- React-Core/RCTWebSocket (0.71.10):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.70.6)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsiexecutor (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-Core/Default (= 0.71.10)
- React-cxxreact (= 0.71.10)
- React-hermes
- React-jsi (= 0.71.10)
- React-jsiexecutor (= 0.71.10)
- React-perflogger (= 0.71.10)
- Yoga
- React-CoreModules (0.70.6):
- React-CoreModules (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.6)
- React-Codegen (= 0.70.6)
- React-Core/CoreModulesHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- React-RCTImage (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-cxxreact (0.70.6):
- RCTTypeSafety (= 0.71.10)
- React-Codegen (= 0.71.10)
- React-Core/CoreModulesHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- React-RCTBlob
- React-RCTImage (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-cxxreact (0.71.10):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.70.6)
- React-jsi (= 0.70.6)
- React-jsinspector (= 0.70.6)
- React-logger (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-runtimeexecutor (= 0.70.6)
- React-jsi (0.70.6):
- React-callinvoker (= 0.71.10)
- React-jsi (= 0.71.10)
- React-jsinspector (= 0.71.10)
- React-logger (= 0.71.10)
- React-perflogger (= 0.71.10)
- React-runtimeexecutor (= 0.71.10)
- React-hermes (0.71.10):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00)
- React-cxxreact (= 0.71.10)
- React-jsi
- React-jsiexecutor (= 0.71.10)
- React-jsinspector (= 0.71.10)
- React-perflogger (= 0.71.10)
- React-jsi (0.71.10):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-jsi/Default (= 0.70.6)
- React-jsi/Default (0.70.6):
- boost (= 1.76.0)
- React-jsiexecutor (0.71.10):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-jsiexecutor (0.70.6):
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-jsinspector (0.70.6)
- React-logger (0.70.6):
- React-cxxreact (= 0.71.10)
- React-jsi (= 0.71.10)
- React-perflogger (= 0.71.10)
- React-jsinspector (0.71.10)
- React-logger (0.71.10):
- glog
- react-native-alarm-notification (2.11.0):
- React
@@ -328,81 +373,99 @@ PODS:
- React-Core
- react-native-version-info (1.1.1):
- React-Core
- react-native-webview (11.26.1):
- react-native-webview (12.4.0):
- React-Core
- React-perflogger (0.70.6)
- React-RCTActionSheet (0.70.6):
- React-Core/RCTActionSheetHeaders (= 0.70.6)
- React-RCTAnimation (0.70.6):
- React-perflogger (0.71.10)
- React-RCTActionSheet (0.71.10):
- React-Core/RCTActionSheetHeaders (= 0.71.10)
- React-RCTAnimation (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.6)
- React-Codegen (= 0.70.6)
- React-Core/RCTAnimationHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTBlob (0.70.6):
- RCTTypeSafety (= 0.71.10)
- React-Codegen (= 0.71.10)
- React-Core/RCTAnimationHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTAppDelegate (0.71.10):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.10):
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.70.6)
- React-Core/RCTBlobHeaders (= 0.70.6)
- React-Core/RCTWebSocket (= 0.70.6)
- React-jsi (= 0.70.6)
- React-RCTNetwork (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTImage (0.70.6):
- React-Codegen (= 0.71.10)
- React-Core/RCTBlobHeaders (= 0.71.10)
- React-Core/RCTWebSocket (= 0.71.10)
- React-jsi (= 0.71.10)
- React-RCTNetwork (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTImage (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.6)
- React-Codegen (= 0.70.6)
- React-Core/RCTImageHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- React-RCTNetwork (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTLinking (0.70.6):
- React-Codegen (= 0.70.6)
- React-Core/RCTLinkingHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTNetwork (0.70.6):
- RCTTypeSafety (= 0.71.10)
- React-Codegen (= 0.71.10)
- React-Core/RCTImageHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- React-RCTNetwork (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTLinking (0.71.10):
- React-Codegen (= 0.71.10)
- React-Core/RCTLinkingHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTNetwork (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.6)
- React-Codegen (= 0.70.6)
- React-Core/RCTNetworkHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTSettings (0.70.6):
- RCTTypeSafety (= 0.71.10)
- React-Codegen (= 0.71.10)
- React-Core/RCTNetworkHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTSettings (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.70.6)
- React-Codegen (= 0.70.6)
- React-Core/RCTSettingsHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-RCTText (0.70.6):
- React-Core/RCTTextHeaders (= 0.70.6)
- React-RCTVibration (0.70.6):
- RCTTypeSafety (= 0.71.10)
- React-Codegen (= 0.71.10)
- React-Core/RCTSettingsHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-RCTText (0.71.10):
- React-Core/RCTTextHeaders (= 0.71.10)
- React-RCTVibration (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.70.6)
- React-Core/RCTVibrationHeaders (= 0.70.6)
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (= 0.70.6)
- React-runtimeexecutor (0.70.6):
- React-jsi (= 0.70.6)
- ReactCommon/turbomodule/core (0.70.6):
- React-Codegen (= 0.71.10)
- React-Core/RCTVibrationHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- React-runtimeexecutor (0.71.10):
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/bridging (0.71.10):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-bridging (= 0.70.6)
- React-callinvoker (= 0.70.6)
- React-Core (= 0.70.6)
- React-cxxreact (= 0.70.6)
- React-jsi (= 0.70.6)
- React-logger (= 0.70.6)
- React-perflogger (= 0.70.6)
- React-callinvoker (= 0.71.10)
- React-Core (= 0.71.10)
- React-cxxreact (= 0.71.10)
- React-jsi (= 0.71.10)
- React-logger (= 0.71.10)
- React-perflogger (= 0.71.10)
- ReactCommon/turbomodule/core (0.71.10):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.10)
- React-Core (= 0.71.10)
- React-cxxreact (= 0.71.10)
- React-jsi (= 0.71.10)
- React-logger (= 0.71.10)
- React-perflogger (= 0.71.10)
- rn-fetch-blob (0.12.0):
- React-Core
- RNCClipboard (1.5.1):
- React-Core
- RNCPushNotificationIOS (1.11.0):
- React-Core
- RNDateTimePicker (7.0.0):
- RNDateTimePicker (7.0.1):
- React-Core
- RNExitApp (1.1.0):
- React
@@ -452,14 +515,15 @@ DEPENDENCIES:
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.125.0)
- FlipperKit/SKIOSNetworkPlugin (= 0.125.0)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- JoplinCommonShareExtension (from `ShareExtension`)
- JoplinRNShareExtension (from `ShareExtension`)
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.1100)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
- React-bridging (from `../node_modules/react-native/ReactCommon`)
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Codegen (from `build/generated/ios`)
- React-Core (from `../node_modules/react-native/`)
@@ -467,6 +531,7 @@ DEPENDENCIES:
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
@@ -490,6 +555,7 @@ DEPENDENCIES:
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
@@ -542,6 +608,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/FBReactNativeSpec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
JoplinCommonShareExtension:
:path: ShareExtension
JoplinRNShareExtension:
@@ -554,8 +622,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../node_modules/react-native/"
React-bridging:
:path: "../node_modules/react-native/ReactCommon"
React-callinvoker:
:path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Codegen:
@@ -566,6 +632,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../node_modules/react-native/ReactCommon/cxxreact"
React-hermes:
:path: "../node_modules/react-native/ReactCommon/hermes"
React-jsi:
:path: "../node_modules/react-native/ReactCommon/jsi"
React-jsiexecutor:
@@ -612,6 +680,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation:
:path: "../node_modules/react-native/Libraries/NativeAnimation"
React-RCTAppDelegate:
:path: "../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../node_modules/react-native/Libraries/Blob"
React-RCTImage:
@@ -658,11 +728,11 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
boost: 57d2868c099736d80fcd648bf211b4431e51a558
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
FBReactNativeSpec: dd1186fd05255e3457baa2f4ca65e94c2cd1e3ac
FBLazyVector: ddb55c55295ea51ed98aa7e2e08add2f826309d5
FBReactNativeSpec: 90fc1a90b4b7a171e0a7c20ea426c1bf6ce4399c
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
@@ -674,24 +744,25 @@ SPEC CHECKSUMS:
FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: d27603b55a48402501ad1928c05411dae9cd6b85
JoplinCommonShareExtension: a8b60b02704d85a7305627912c0240e94af78db7
JoplinRNShareExtension: 485f3e6dad83b7b77f1572eabc249f869ee55c02
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: e1866f61af7049eb3d8e08e8b133abd38bc1ca7a
RCTTypeSafety: 27c2ac1b00609a432ced1ae701247593f07f901e
React: bb3e06418d2cc48a84f9666a576c7b38e89cd7db
React-bridging: 572502ec59c9de30309afdc4932e278214288913
React-callinvoker: 6b708b79c69f3359d42f1abb4663f620dbd4dadf
React-Codegen: 74e1cd7cee692a8b983c18df3274b5e749de07c8
React-Core: b587d0a624f9611b0e032505f3d6f25e8daa2bee
React-CoreModules: c6ff48b985e7aa622e82ca51c2c353c7803eb04e
React-cxxreact: ade3d9e63c599afdead3c35f8a8bd12b3da6730b
React-jsi: 5a3952e0c6d57460ad9ee2c905025b4c28f71087
React-jsiexecutor: b4a65947391c658450151275aa406f2b8263178f
React-jsinspector: 60769e5a0a6d4b32294a2456077f59d0266f9a8b
React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: 8ef706f91e2b643cd32c26a57700b5f24fab0585
RCTTypeSafety: 5fbddd8eb9242b91ac0d901c01da3673f358b1b7
React: e5d2d559e89d256a1d6da64d51adaecda9c8ddae
React-callinvoker: 352ecbafbdccca5fdf4aed99c98ae5b7fc28e39b
React-Codegen: fa660a71e24078b2e52a62ecc2f3048c2f8ae6d7
React-Core: 4ec45c2d537fe58e6d878bec6a13e3e2bed9c182
React-CoreModules: 63f7f9fda3d4b214040a80e3f47ab4fb9a3e88e6
React-cxxreact: 1a729807190ebf98ce5fb0c3d2ed211e8b5f2f87
React-hermes: eb93eb6e7921ecd4abcc6e741b327f40763e850f
React-jsi: 1995961abdff0c9af9aae8a6b24468f21811000e
React-jsiexecutor: 4bb480a183a354e4dbfb1012936b1a2bb9357de7
React-jsinspector: cdc854f8b13abd202afa54bc12578e5afb9cfae1
React-logger: ef2269b3afa6ba868da90496c3e17a4ec4f4cee0
react-native-alarm-notification: 26527410a6162d07a9dc57f4bbc62e94ff48e65d
react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f
react-native-document-picker: 69ca2094d8780cfc1e7e613894d15290fdc54bba
@@ -707,23 +778,24 @@ SPEC CHECKSUMS:
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
react-native-webview: 9f111dfbcfc826084d6c507f569e5e03342ee1c1
React-perflogger: 8c79399b0500a30ee8152d0f9f11beae7fc36595
React-RCTActionSheet: 7316773acabb374642b926c19aef1c115df5c466
React-RCTAnimation: 5341e288375451297057391227f691d9b2326c3d
React-RCTBlob: b0615fc2daf2b5684ade8fadcab659f16f6f0efa
React-RCTImage: 6487b9600f268ecedcaa86114d97954d31ad4750
React-RCTLinking: c8018ae9ebfefcec3839d690d4725f8d15e4e4b3
React-RCTNetwork: 8aa63578741e0fe1205c28d7d4b40dbfdabce8a8
React-RCTSettings: d00c15ad369cd62242a4dfcc6f277912b4a84ed3
React-RCTText: f532e5ca52681ecaecea452b3ad7a5b630f50d75
React-RCTVibration: c75ceef7aa60a33b2d5731ebe5800ddde40cefc4
React-runtimeexecutor: 15437b576139df27635400de0599d9844f1ab817
ReactCommon: 349be31adeecffc7986a0de875d7fb0dcf4e251c
react-native-webview: 65f1143983cfeaedf02fd25b2621d3f4a37075de
React-perflogger: 217095464d5c4bb70df0742fa86bf2a363693468
React-RCTActionSheet: 8deae9b85a4cbc6a2243618ea62a374880a2c614
React-RCTAnimation: 59c62353a8b59ce206044786c5d30e4754bffa64
React-RCTAppDelegate: ef66a6904141fca96bffb00fac327a482b575f19
React-RCTBlob: 8e518bae3d6ca97ffb7088da673fbbc53042d94d
React-RCTImage: 36c0324ff499802b9874d6803ca72026e90434f6
React-RCTLinking: 401aec3a01b18c2c8ed93bf3a6758b87e617c58d
React-RCTNetwork: cb25b9f2737c3aa2cde0fe0bd7ff7fabf7bf9ad0
React-RCTSettings: cb6ae9f656e1c880500c2ecbe8e72861c2262afa
React-RCTText: 7404fd01809244d79d456f92cfe6f9fbadf69209
React-RCTVibration: d13cc2d63286c633393d3a7f6f607cc2a09ec011
React-runtimeexecutor: a9a1cd79996c9a0846e3232ecb25c64e1cc0172e
ReactCommon: 65718685d4095d06b4b1af8042e12f1df2925c31
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCPushNotificationIOS: 64218f3c776c03d7408284a819b2abfda1834bc8
RNDateTimePicker: 66cb21772d462d02efc6826adac37f1fd40984cf
RNDateTimePicker: e073697ac3e8a378968d68ab0581fef542b8af8a
RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
@@ -733,9 +805,9 @@ SPEC CHECKSUMS:
RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: 99caf8d5ab45e9d637ee6e0174ec16fbbb01bcfc
Yoga: e7ea9e590e27460d28911403b894722354d73479
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 0235ffbfa2e655de806a80d996148182dd493d8d
PODFILE CHECKSUM: 3b2cace838120977b5b54871752c9dddf5a11cea
COCOAPODS: 1.11.3

View File

@@ -23,7 +23,7 @@
"@joplin/react-native-saf-x": "~2.11",
"@joplin/renderer": "~2.11",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/datetimepicker": "7.0.0",
"@react-native-community/datetimepicker": "7.0.1",
"@react-native-community/geolocation": "2.1.0",
"@react-native-community/netinfo": "9.3.10",
"@react-native-community/push-notification-ios": "1.11.0",
@@ -40,7 +40,7 @@
"prop-types": "15.8.1",
"punycode": "2.3.0",
"react": "18.2.0",
"react-native": "0.70.6",
"react-native": "0.71.10",
"react-native-action-button": "2.8.5",
"react-native-camera": "4.2.1",
"react-native-dialogbox": "0.6.10",
@@ -68,7 +68,7 @@
"react-native-vector-icons": "9.2.0",
"react-native-version-info": "1.1.1",
"react-native-vosk": "0.1.12",
"react-native-webview": "11.26.1",
"react-native-webview": "12.4.0",
"react-redux": "7.2.9",
"redux": "4.2.1",
"rn-fetch-blob": "0.12.0",
@@ -79,8 +79,9 @@
"url": "0.11.0"
},
"devDependencies": {
"@babel/core": "7.16.0",
"@babel/runtime": "7.16.3",
"@babel/core": "7.20.2",
"@babel/preset-env": "7.20.2",
"@babel/runtime": "7.20.0",
"@codemirror/commands": "6.2.2",
"@codemirror/lang-cpp": "6.0.2",
"@codemirror/lang-html": "6.4.3",
@@ -96,10 +97,13 @@
"@codemirror/view": "6.9.3",
"@joplin/tools": "~2.11",
"@lezer/highlight": "1.1.4",
"@tsconfig/react-native": "2.0.2",
"@types/fs-extra": "11.0.1",
"@types/jest": "29.5.1",
"@types/react": "18.0.24",
"@types/react-native": "0.70.6",
"@types/react-redux": "7.1.25",
"babel-jest": "29.2.1",
"babel-plugin-module-resolver": "4.1.0",
"execa": "4.1.0",
"fs-extra": "11.1.1",
@@ -109,7 +113,7 @@
"jetifier": "2.0.0",
"jsdom": "21.1.2",
"md5-file": "5.0.0",
"metro-react-native-babel-preset": "0.72.3",
"metro-react-native-babel-preset": "0.73.9",
"nodemon": "2.0.22",
"ts-jest": "29.1.0",
"ts-loader": "9.4.2",

View File

@@ -26,7 +26,8 @@ export interface Recorder {
export const getVosk = async () => {
if (vosk_) return vosk_;
vosk_ = new Vosk();
await vosk_.loadModel('model-fr-fr');
const result = await vosk_.loadModel('model-fr-fr');
logger.info('getVosk:', result);
return vosk_;
};

View File

@@ -1,4 +1,7 @@
import Logger from '@joplin/lib/Logger';
const { Platform, PermissionsAndroid } = require('react-native');
const logger = Logger.create('checkPermissions');
type rationale = {
title: string;
@@ -9,11 +12,14 @@ type rationale = {
};
export default async (permissions: string, rationale?: rationale) => {
if (Platform.OS !== 'android') return true;
// On iOS, permissions are prompted for by the system, so here we assume it's granted.
if (Platform.OS !== 'android') return PermissionsAndroid.RESULTS.GRANTED;
let result = await PermissionsAndroid.check(permissions);
logger.info('Checked permission:', result);
if (result !== PermissionsAndroid.RESULTS.GRANTED) {
result = await PermissionsAndroid.request(permissions, rationale);
logger.info('Requested permission:', result);
}
return result;
};

View File

@@ -1,4 +1,5 @@
import { ModelType } from '../../../BaseModel';
import Plugin from '../Plugin';
import { Path } from './types';
/**
* This module provides access to the Joplin data API: https://joplinapp.org/api/references/rest_api/
@@ -39,6 +40,8 @@ import { Path } from './types';
export default class JoplinData {
private api_;
private pathSegmentRegex_;
private plugin;
constructor(plugin: Plugin);
private serializeApiBody;
private pathToString;
get(path: Path, query?: any): Promise<any>;
@@ -47,4 +50,24 @@ export default class JoplinData {
delete(path: Path, query?: any): Promise<any>;
itemType(itemId: string): Promise<ModelType>;
resourcePath(resourceId: string): Promise<string>;
/**
* Gets an item user data. User data are key/value pairs. The `key` can be any
* arbitrary string, while the `value` can be of any type supported by
* [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description)
*
* User data is synchronised across devices, and each value wil be merged based on their timestamp:
*
* - If value is modified by client 1, then modified by client 2, it will take the value from client 2
* - If value is modified by client 1, then deleted by client 2, the value will be deleted after merge
* - If value is deleted by client 1, then updated by client 2, the value will be restored and set to the value from client 2 after merge
*/
userDataGet<T>(itemType: ModelType, itemId: string, key: string): Promise<T>;
/**
* Sets a note user data. See {@link JoplinData.userDataGet} for more details.
*/
userDataSet<T>(itemType: ModelType, itemId: string, key: string, value: T): Promise<void>;
/**
* Deletes a note user data. See {@link JoplinData.userDataGet} for more details.
*/
userDataDelete(itemType: ModelType, itemId: string, key: string): Promise<void>;
}

View File

@@ -1,11 +1,13 @@
#!/bin/bash
git pull
set -e
if [[ -n $(git status --porcelain) ]]; then
echo "There are changes in the repo"
exit 1
fi
# git pull
# if [[ -n $(git status --porcelain) ]]; then
# echo "There are changes in the repo"
# exit 1
# fi
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CLI_DIR="$SCRIPT_DIR/../app-cli"
@@ -13,7 +15,7 @@ LIB_DIR="$SCRIPT_DIR/../lib"
./updateTypes.sh
yarn link
npm link
"$CLI_DIR/tests/support/plugins/updatePlugins.sh"

View File

@@ -4,6 +4,7 @@ import Database from './database';
import uuid from './uuid';
import time from './time';
import JoplinDatabase, { TableField } from './JoplinDatabase';
import { LoadOptions } from './models/utils/types';
const Mutex = require('async-mutex').Mutex;
// New code should make use of this enum
@@ -254,7 +255,7 @@ class BaseModel {
});
}
public static load(id: string, options: any = null) {
public static load(id: string, options: LoadOptions = null) {
return this.loadByField('id', id, options);
}
@@ -352,7 +353,7 @@ class BaseModel {
});
}
public static loadByField(fieldName: string, fieldValue: any, options: any = null) {
public static loadByField(fieldName: string, fieldValue: any, options: LoadOptions = null) {
if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
if (!options.fields) options.fields = '*';
@@ -361,7 +362,7 @@ class BaseModel {
return this.modelSelectOne(sql, [fieldValue]);
}
public static loadByFields(fields: any, options: any = null) {
public static loadByFields(fields: any, options: LoadOptions = null) {
if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
if (!options.fields) options.fields = '*';

View File

@@ -354,7 +354,7 @@ export default class JoplinDatabase extends Database {
// must be set in the synchronizer too.
// Note: v16 and v17 don't do anything. They were used to debug an issue.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42];
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@@ -917,6 +917,13 @@ export default class JoplinDatabase extends Database {
queries.push(this.addMigrationFile(42));
}
if (targetVersion === 43) {
queries.push('ALTER TABLE `notes` ADD COLUMN `user_data` TEXT NOT NULL DEFAULT ""');
queries.push('ALTER TABLE `tags` ADD COLUMN `user_data` TEXT NOT NULL DEFAULT ""');
queries.push('ALTER TABLE `folders` ADD COLUMN `user_data` TEXT NOT NULL DEFAULT ""');
queries.push('ALTER TABLE `resources` ADD COLUMN `user_data` TEXT NOT NULL DEFAULT ""');
}
const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] };
queries.push(updateVersionQuery);

View File

@@ -11,6 +11,7 @@ import ShareService from '../services/share/ShareService';
import itemCanBeEncrypted from './utils/itemCanBeEncrypted';
import { getEncryptionEnabled } from '../services/synchronizer/syncInfoUtils';
import JoplinError from '../JoplinError';
import { LoadOptions } from './utils/types';
const { sprintf } = require('sprintf-js');
const moment = require('moment');
@@ -227,9 +228,10 @@ export default class BaseItem extends BaseModel {
return ItemClass.loadByField(field, value);
}
public static loadItem(itemType: ModelType, id: string) {
public static loadItem(itemType: ModelType, id: string, options: LoadOptions = null) {
if (!options) options = {};
const ItemClass = this.itemClass(itemType);
return ItemClass.load(id);
return ItemClass.load(id, options);
}
public static deleteItem(itemType: ModelType, id: string) {

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