1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Merge remote-tracking branch 'origin/dev' into binary-sign-default-plugins

This commit is contained in:
palerdot 2023-04-11 18:09:21 +05:30
commit ed58ed91fd
32 changed files with 495 additions and 239 deletions

View File

@ -405,6 +405,7 @@ packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/app-nav.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/getResponsiveValue.js
packages/app-mobile/components/getResponsiveValue.test.js

View File

@ -93,6 +93,7 @@ jobs:
APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}

1
.gitignore vendored
View File

@ -392,6 +392,7 @@ packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/app-nav.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/getResponsiveValue.js
packages/app-mobile/components/getResponsiveValue.test.js

View File

@ -126,6 +126,7 @@ A community maintained list of these distributions can be found here: [Unofficia
- [Writing a technical spec](https://github.com/laurent22/joplin/blob/dev/readme/technical_spec.md)
- [Desktop application styling](https://github.com/laurent22/joplin/blob/dev/readme/spec/desktop_styling.md)
- [Note History spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/history.md)
- [Synchronisation spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync.md)
- [Sync Lock spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md)
- [Synchronous Scroll spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_scroll.md)
- [Plugin Architecture spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/plugins.md)

View File

@ -1,26 +1,52 @@
const gulp = require('gulp');
const utils = require('./packages/tools/gulp/utils');
const execa = require('execa');
const { stdout } = require('process');
const execCommand = async (executableName, args, options = null) => {
options = {
showInput: true,
showStdout: true,
showStderr: true,
quiet: false,
...options,
};
if (options.quiet) {
options.showInput = false;
options.showStdout = false;
options.showStderr = false;
}
if (options.showInput) {
stdout.write(`> ${executableName} ${args.join(' ')}\n`);
}
const promise = execa(executableName, args);
if (options.showStdout && promise.stdout) promise.stdout.pipe(process.stdout);
if (options.showStderr && promise.stderr) promise.stderr.pipe(process.stderr);
const result = await promise;
return result.stdout.trim();
};
const tasks = {
updateIgnoredTypeScriptBuild: require('./packages/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
buildCommandIndex: require('./packages/tools/gulp/tasks/buildCommandIndex'),
completePublishAll: {
fn: async () => {
await utils.execCommandVerbose('git', ['add', '-A']);
await utils.execCommandVerbose('git', ['commit', '-m', 'Releasing sub-packages']);
await execCommand('git', ['add', '-A']);
await execCommand('git', ['commit', '-m', 'Releasing sub-packages']);
// Lerna does some unnecessary auth check that doesn't work with
// automation tokens, thus the --no-verify-access. Automation token
// is still used for access when publishing even with this flag
// (publishing would fail otherwise).
// https://github.com/lerna/lerna/issues/2788
await utils.execCommandVerbose('lerna', ['publish', 'from-package', '-y', '--no-verify-access']);
await execCommand('lerna', ['publish', 'from-package', '-y', '--no-verify-access']);
await utils.execCommandVerbose('yarn', ['install']);
await utils.execCommandVerbose('git', ['add', '-A']);
await utils.execCommandVerbose('git', ['commit', '-m', 'Lock file']);
await execCommand('yarn', ['install']);
await execCommand('git', ['add', '-A']);
await execCommand('git', ['commit', '-m', 'Lock file']);
await utils.execCommandVerbose('git', ['push']);
await execCommand('git', ['push']);
},
},
build: {
@ -33,12 +59,14 @@ const tasks = {
// faster, especially when having to rebuild after adding a
// dependency.
if (process.env.BUILD_SEQUENCIAL === '1') {
await utils.execCommandVerbose('yarn', ['run', 'buildSequential']);
await execCommand('yarn', ['run', 'buildSequential']);
} else {
await utils.execCommandVerbose('yarn', ['run', 'buildParallel']);
await execCommand('yarn', ['run', 'buildParallel']);
}
},
},
};
utils.registerGulpTasks(gulp, tasks);
for (const taskName in tasks) {
gulp.task(taskName, tasks[taskName].fn);
}

View File

@ -15,7 +15,7 @@
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
"buildCommandIndex": "gulp buildCommandIndex",
"buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
"updateNews": "node ./packages/tools/website/updateNews",
@ -53,7 +53,7 @@
"test-ci": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test-ci",
"test": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test",
"tsc": "yarn workspaces foreach --parallel --verbose --interlaced run tsc",
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
"updateIgnored": "node packages/tools/gulp/tasks/updateIgnoredTypeScriptBuildRun.js",
"updatePluginTypes": "./packages/generator-joplin/updateTypes.sh",
"watch": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 999 run watch",
"watchWebsite": "nodemon --verbose --watch Assets/WebsiteAssets --watch packages/tools/website --watch packages/tools/website/utils --ext md,ts,js,mustache,css,tsx,gif,png,svg --exec \"node packages/tools/website/build.js && http-server --port 8077 ../joplin-website/docs -a localhost\""
@ -64,6 +64,7 @@
}
},
"devDependencies": {
"@joplin/utils": "~2.11",
"@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
@ -74,6 +75,7 @@
"eslint-plugin-jest": "27.2.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-react": "7.32.0",
"execa": "5.1.1",
"fs-extra": "11.1.1",
"glob": "8.1.0",
"gulp": "4.0.2",

View File

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "2.11.0",
"version": "2.11.2",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplinapp.org",
"content_security_policy": "script-src 'self'; object-src 'self'",

View File

@ -126,4 +126,4 @@
"react-app"
]
}
}
}

View File

@ -150,8 +150,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097685
versionName "2.11.0"
versionCode 2097687
versionName "2.11.2"
// ndk {
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
// }

View File

@ -3,62 +3,57 @@
*/
import { EditorSettings } from '../types';
import { initCodeMirror } from './CodeMirror';
import { themeStyle } from '@joplin/lib/theme';
import Setting from '@joplin/lib/models/Setting';
import { forceParsing } from '@codemirror/language';
import loadLangauges from './testUtil/loadLanguages';
// Randomly fails on:
//
// > 42 | expect(headerLineContent.textContent).toBe(headerLineText);
//
import { expect, describe, it } from '@jest/globals';
const createEditorSettings = (themeId: number) => {
const themeData = themeStyle(themeId);
const editorSettings: EditorSettings = {
katexEnabled: true,
spellcheckEnabled: true,
themeId,
themeData,
};
return editorSettings;
};
describe('CodeMirror', () => {
it('should succeed', async () => {
expect(1).toBe(1);
// This checks for a regression -- occasionally, when updating packages,
// syntax highlighting in the CodeMirror editor stops working. This is usually
// fixed by
// 1. removing all `@codemirror/` and `@lezer/` dependencies from yarn.lock,
// 2. upgrading all CodeMirror packages to the latest versions in package.json, and
// 3. re-running `yarn install`.
//
// See https://github.com/laurent22/joplin/issues/7253
it('should give headings a different style', async () => {
const headerLineText = '# Testing...';
const initialText = `${headerLineText}\nThis is a test.`;
const editorSettings = createEditorSettings(Setting.THEME_LIGHT);
await loadLangauges();
const editor = initCodeMirror(document.body, initialText, editorSettings);
// Force the generation of the syntax tree now.
forceParsing(editor.editor);
// CodeMirror nests the tag that styles the header within .cm-headerLine:
// <div class='cm-headerLine'><span class='someclass'>Testing...</span></div>
const headerLineContent = document.body.querySelector('.cm-headerLine > span')!;
expect(headerLineContent.textContent).toBe(headerLineText);
const style = getComputedStyle(headerLineContent);
expect(style.borderBottom).not.toBe('');
expect(style.fontSize).toBe('1.6em');
});
});
// import { EditorSettings } from '../types';
// import { initCodeMirror } from './CodeMirror';
// import { themeStyle } from '@joplin/lib/theme';
// import Setting from '@joplin/lib/models/Setting';
// import { forceParsing } from '@codemirror/language';
// import loadLangauges from './testUtil/loadLanguages';
// const createEditorSettings = (themeId: number) => {
// const themeData = themeStyle(themeId);
// const editorSettings: EditorSettings = {
// katexEnabled: true,
// spellcheckEnabled: true,
// themeId,
// themeData,
// };
// return editorSettings;
// };
// describe('CodeMirror', () => {
// it('should give headings a different style', async () => {
// const headerLineText = '# Testing...';
// const initialText = `${headerLineText}\nThis is a test.`;
// const editorSettings = createEditorSettings(Setting.THEME_LIGHT);
// await loadLangauges();
// const editor = initCodeMirror(document.body, initialText, editorSettings);
// // Force the generation of the syntax tree now.
// forceParsing(editor.editor);
// // CodeMirror nests the tag that styles the header within .cm-headerLine:
// // <div class='cm-headerLine'><span class='someclass'>Testing...</span></div>
// const headerLineContent = document.body.querySelector('.cm-headerLine > span')!;
// expect(headerLineContent.textContent).toBe(headerLineText);
// const style = getComputedStyle(headerLineContent);
// expect(style.borderBottom).not.toBe('');
// expect(style.fontSize).toBe('1.6em');
// });
// });

View File

@ -2,9 +2,12 @@ const React = require('react');
import Setting from '@joplin/lib/models/Setting';
import { useEffect, useMemo, useState } from 'react';
import { View, Dimensions, Alert, Button } from 'react-native';
import FingerprintScanner from 'react-native-fingerprint-scanner';
import { SensorInfo } from './sensorInfo';
import { _ } from '@joplin/lib/locale';
import Logger from '@joplin/lib/Logger';
import biometricAuthenticate from './biometricAuthenticate';
const logger = Logger.create('BiometricPopup');
interface Props {
themeId: number;
@ -18,23 +21,31 @@ export default (props: Props) => {
// doesn't work properly, we disable it. We only want the user to enable the
// feature after they've read the description in the config screen.
const [initialPromptDone, setInitialPromptDone] = useState(true); // useState(Setting.value('security.biometricsInitialPromptDone'));
const [display, setDisplay] = useState(!!props.sensorInfo.supportedSensors && (props.sensorInfo.enabled || !initialPromptDone));
const [display, setDisplay] = useState(props.sensorInfo.enabled || !initialPromptDone);
const [tryBiometricsCheck, setTryBiometricsCheck] = useState(initialPromptDone);
logger.info('Render start');
logger.info('initialPromptDone', initialPromptDone);
logger.info('display', display);
logger.info('tryBiometricsCheck', tryBiometricsCheck);
logger.info('props.sensorInfo', props.sensorInfo);
useEffect(() => {
if (!display || !tryBiometricsCheck) return;
const biometricsCheck = async () => {
logger.info('biometricsCheck: start');
try {
await FingerprintScanner.authenticate({ description: _('Verify your identity') });
setTryBiometricsCheck(false);
await biometricAuthenticate();
setDisplay(false);
} catch (error) {
Alert.alert(_('Could not verify your identify'), error.message);
setTryBiometricsCheck(false);
} finally {
FingerprintScanner.release();
Alert.alert(error.message);
}
setTryBiometricsCheck(false);
logger.info('biometricsCheck: end');
};
void biometricsCheck();
@ -45,6 +56,9 @@ export default (props: Props) => {
if (!display) return;
const complete = (enableBiometrics: boolean) => {
logger.info('complete: start');
logger.info('complete: enableBiometrics:', enableBiometrics);
setInitialPromptDone(true);
Setting.setValue('security.biometricsInitialPromptDone', true);
Setting.setValue('security.biometricsEnabled', enableBiometrics);
@ -59,6 +73,8 @@ export default (props: Props) => {
type: 'BIOMETRICS_DONE_SET',
value: true,
});
logger.info('complete: end');
};
Alert.alert(
@ -77,7 +93,7 @@ export default (props: Props) => {
},
]
);
}, [initialPromptDone, props.sensorInfo.supportedSensors, display, props.dispatch]);
}, [initialPromptDone, display, props.dispatch]);
const windowSize = useMemo(() => {
return {
@ -87,12 +103,18 @@ export default (props: Props) => {
}, []);
useEffect(() => {
logger.info('effect 1: start');
if (!display) {
logger.info('effect 1: display', display);
props.dispatch({
type: 'BIOMETRICS_DONE_SET',
value: true,
});
}
logger.info('effect 1: end');
}, [display, props.dispatch]);
const renderTryAgainButton = () => {

View File

@ -0,0 +1,28 @@
import Logger from '@joplin/lib/Logger';
import FingerprintScanner, { Errors } from 'react-native-fingerprint-scanner';
import { _ } from '@joplin/lib/locale';
const logger = Logger.create('biometricAuthenticate');
export default async () => {
try {
logger.info('Authenticate...');
await FingerprintScanner.authenticate({ description: _('Verify your identity') });
logger.info('Authenticate done');
} catch (error) {
const errorName = (error as Errors).name;
let errorMessage = error.message;
if (errorName === 'FingerprintScannerNotEnrolled' || errorName === 'FingerprintScannerNotAvailable') {
errorMessage = _('Biometric unlock is not setup on the device. Please set it up in order to unlock Joplin. If the device is on lockout, consider switching it off and on to reset biometrics scanning.');
}
error.message = _('Could not verify your identify: %s', errorMessage);
logger.warn(error);
throw error;
} finally {
FingerprintScanner.release();
}
};

View File

@ -1,5 +1,7 @@
import Logger from '@joplin/lib/Logger';
import Setting from '@joplin/lib/models/Setting';
import FingerprintScanner from 'react-native-fingerprint-scanner';
const logger = Logger.create('sensorInfo');
export interface SensorInfo {
enabled: boolean;
@ -12,6 +14,9 @@ export default async (): Promise<SensorInfo> => {
// FingerprintScanner scanner calls, since it seems they can fail and freeze
// the app.
logger.info('Start');
logger.info('security.biometricsEnabled', Setting.value('security.biometricsEnabled'));
if (!Setting.value('security.biometricsEnabled')) {
return {
enabled: false,
@ -24,7 +29,21 @@ export default async (): Promise<SensorInfo> => {
let supportedSensors = '';
try {
logger.info('Getting isSensorAvailable...');
// Note: If `isSensorAvailable()` doesn't return anything, it seems we
// could assume that biometrics are not setup on the device, and thus we
// can unlock the app. However that's not always correct - on some
// devices (eg Galaxy S22), `isSensorAvailable()` will return nothing if
// the device is on lockout - i.e. if the user gave the wrong
// fingerprint multiple times.
//
// So we definitely can't unlock the app in that case, and it means
// `isSensorAvailable()` is pretty much useless. Instead we ask for
// fingerprint when the user turns on the feature and at that point we
// know if the device supports biometrics or not.
const result = await FingerprintScanner.isSensorAvailable();
logger.info('isSensorAvailable result', result);
supportedSensors = result;
if (result) {
@ -34,7 +53,7 @@ export default async (): Promise<SensorInfo> => {
}
}
} catch (error) {
console.warn('Could not check for biometrics sensor:', error);
logger.warn('Could not check for biometrics sensor:', error);
Setting.setValue('security.biometricsSupportedSensors', '');
}

View File

@ -23,6 +23,7 @@ const { themeStyle } = require('../global-style.js');
const shared = require('@joplin/lib/components/shared/config-shared.js');
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
import { openDocumentTree } from '@joplin/react-native-saf-x';
import biometricAuthenticate from '../biometrics/biometricAuthenticate';
class ConfigScreenComponent extends BaseScreenComponent {
public static navigationOptions(): any {
@ -463,7 +464,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
<Text key="label" style={this.styles().switchSettingText}>
{label}
</Text>
<Switch key="control" style={this.styles().switchSettingControl} trackColor={{ false: theme.dividerColor }} value={value} onValueChange={(value: any) => updateSettingValue(key, value)} />
<Switch key="control" style={this.styles().switchSettingControl} trackColor={{ false: theme.dividerColor }} value={value} onValueChange={(value: any) => void updateSettingValue(key, value)} />
</View>
{descriptionComp}
</View>
@ -474,13 +475,39 @@ class ConfigScreenComponent extends BaseScreenComponent {
return !hasDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder;
}
private async handleSetting(key: string, value: any): Promise<boolean> {
// When the user tries to enable biometrics unlock, we ask for the
// fingerprint or Face ID, and if it's correct we save immediately. If
// it's not, we don't turn on the setting.
if (key === 'security.biometricsEnabled' && !!value) {
try {
await biometricAuthenticate();
shared.updateSettingValue(this, key, value);
await this.saveButton_press();
} catch (error) {
shared.updateSettingValue(this, key, false);
Alert.alert(error.message);
}
return true;
}
if (key === 'security.biometricsEnabled' && !value) {
shared.updateSettingValue(this, key, value);
await this.saveButton_press();
return true;
}
return false;
}
public settingToComponent(key: string, value: any) {
const themeId = this.props.themeId;
const theme = themeStyle(themeId);
const output: any = null;
const updateSettingValue = (key: string, value: any) => {
return shared.updateSettingValue(this, key, value);
const updateSettingValue = async (key: string, value: any) => {
const handled = await this.handleSetting(key, value);
if (!handled) shared.updateSettingValue(this, key, value);
};
const md = Setting.settingMetadata(key);
@ -517,7 +544,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
fontSize: theme.fontSize,
}}
onValueChange={(itemValue: string) => {
updateSettingValue(key, itemValue);
void updateSettingValue(key, itemValue);
}}
/>
</View>
@ -553,7 +580,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
</Text>
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
<Text style={this.styles().sliderUnits}>{unitLabel}</Text>
<Slider key="control" style={{ flex: 1 }} step={md.step} minimumValue={minimum} maximumValue={maximum} value={value} onValueChange={value => updateSettingValue(key, value)} />
<Slider key="control" style={{ flex: 1 }} step={md.step} minimumValue={minimum} maximumValue={maximum} value={value} onValueChange={value => void updateSettingValue(key, value)} />
</View>
</View>
);
@ -577,7 +604,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
<Text key="label" style={this.styles().settingText}>
{md.label()}
</Text>
<TextInput autoCorrect={false} autoComplete="off" selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} autoCapitalize="none" key="control" style={this.styles().settingControl} value={value} onChangeText={(value: any) => updateSettingValue(key, value)} secureTextEntry={!!md.secure} />
<TextInput autoCorrect={false} autoComplete="off" selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} autoCapitalize="none" key="control" style={this.styles().settingControl} value={value} onChangeText={(value: any) => void updateSettingValue(key, value)} secureTextEntry={!!md.secure} />
</View>
);
} else {

View File

@ -328,7 +328,7 @@ PODS:
- React-Core
- react-native-image-resizer (1.4.5):
- React-Core
- react-native-netinfo (9.3.7):
- react-native-netinfo (9.3.8):
- React-Core
- react-native-rsa-native (2.0.5):
- React
@ -722,7 +722,7 @@ SPEC CHECKSUMS:
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
react-native-image-picker: ec9b713e248760bfa0f879f0715391de4651a7cb
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
react-native-netinfo: fbc23bc2fe217155d85f2f7e0644b1654df8029b
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc

View File

@ -25,7 +25,7 @@
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/datetimepicker": "6.7.5",
"@react-native-community/geolocation": "2.1.0",
"@react-native-community/netinfo": "9.3.7",
"@react-native-community/netinfo": "9.3.8",
"@react-native-community/push-notification-ios": "1.10.1",
"@react-native-community/slider": "4.4.2",
"assert-browserify": "2.0.0",
@ -54,7 +54,7 @@
"react-native-image-picker": "5.3.1",
"react-native-image-resizer": "1.4.5",
"react-native-modal-datetime-picker": "14.0.1",
"react-native-paper": "5.4.1",
"react-native-paper": "5.5.0",
"react-native-popup-menu": "0.16.1",
"react-native-quick-actions": "0.3.13",
"react-native-rsa-native": "2.0.5",
@ -79,19 +79,19 @@
"devDependencies": {
"@babel/core": "7.16.0",
"@babel/runtime": "7.16.3",
"@codemirror/commands": "6.1.2",
"@codemirror/commands": "6.2.2",
"@codemirror/lang-cpp": "6.0.2",
"@codemirror/lang-html": "6.4.0",
"@codemirror/lang-html": "6.4.3",
"@codemirror/lang-java": "6.0.1",
"@codemirror/lang-javascript": "6.1.1",
"@codemirror/lang-markdown": "6.0.5",
"@codemirror/lang-javascript": "6.1.5",
"@codemirror/lang-markdown": "6.1.0",
"@codemirror/lang-php": "6.0.1",
"@codemirror/lang-rust": "6.0.1",
"@codemirror/language": "6.3.2",
"@codemirror/legacy-modes": "6.3.1",
"@codemirror/search": "6.2.3",
"@codemirror/state": "6.1.4",
"@codemirror/view": "6.7.1",
"@codemirror/language": "6.6.0",
"@codemirror/legacy-modes": "6.3.2",
"@codemirror/search": "6.3.0",
"@codemirror/state": "6.2.0",
"@codemirror/view": "6.9.3",
"@joplin/tools": "~2.11",
"@lezer/highlight": "1.1.4",
"@types/fs-extra": "9.0.13",

View File

@ -501,7 +501,7 @@ async function initialize(dispatch: Function) {
if (Setting.value('env') === 'prod') {
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
} else {
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-1') });
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-3') });
// await db.clearForTesting();
}
@ -984,6 +984,10 @@ class AppComponent extends React.Component {
const biometricIsEnabled = !!this.state.sensorInfo && this.state.sensorInfo.enabled;
const shouldShowMainContent = !biometricIsEnabled || this.props.biometricsDone;
logger.info('root.biometrics: biometricIsEnabled', biometricIsEnabled);
logger.info('root.biometrics: shouldShowMainContent', shouldShowMainContent);
logger.info('root.biometrics: this.state.sensorInfo', this.state.sensorInfo);
const mainContent = (
<View style={{ flex: 1, backgroundColor: theme.backgroundColor }}>
<SideMenu

View File

@ -24,7 +24,7 @@
},
"dependencies": {
"chalk": "2.4.2",
"slugify": "1.6.5",
"slugify": "1.6.6",
"yeoman-generator": "5.8.0",
"yosay": "2.0.2"
},

View File

@ -69,7 +69,7 @@
"moment": "2.29.4",
"multiparty": "4.2.3",
"mustache": "4.2.0",
"nanoid": "3.3.4",
"nanoid": "3.3.6",
"node-fetch": "2.6.7",
"node-notifier": "10.0.1",
"node-persist": "3.1.3",

View File

@ -110,6 +110,8 @@ export default class ReportService {
let syncedCount = 0;
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
const d = BaseItem.syncItemDefinitions_[i];
// ref: https://github.com/laurent22/joplin/issues/7940#issuecomment-1473709148
if (d.className === 'MasterKey') continue;
const ItemClass = BaseItem.getClass(d.className);
const o = {
total: await ItemClass.count(),

View File

@ -0,0 +1,12 @@
// Allow running that task "buildCommandIndex" without gulp
const task = require('./buildCommandIndex.js');
const main = async () => {
await task.fn();
};
main().catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -0,0 +1,12 @@
// Allow running that task "updateIgnoredTypeScriptBuild" without gulp
const task = require('./updateIgnoredTypeScriptBuild.js');
const main = async () => {
await task.fn();
};
main().catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -1,10 +1,8 @@
import { readFile } from 'fs-extra';
import { rootDir } from '@joplin/utils';
import { getRootDir } from '@joplin/utils';
import { fetchWithRetry } from '@joplin/utils/net';
import { githubOauthToken } from '../tool-utils';
const sponsorsPath = `${rootDir}/packages/tools/sponsors.json`;
export interface GithubSponsor {
name: string;
id: string;
@ -23,6 +21,7 @@ export interface OrgSponsor {
}
export const loadSponsors = async (): Promise<Sponsors> => {
const sponsorsPath = `${await getRootDir()}/packages/tools/sponsors.json`;
const output: Sponsors = JSON.parse(await readFile(sponsorsPath, 'utf8'));
output.orgs = output.orgs.map(o => {

View File

@ -39,7 +39,7 @@ export default async (command: string | string[], options: ExecCommandOptions |
args.splice(0, 1);
const promise = execa(executableName, args);
if (options.showStdout && promise.stdout) promise.stdout.pipe(process.stdout);
if (options.showStderr && promise.stdout) promise.stdout.pipe(process.stderr);
if (options.showStderr && promise.stderr) promise.stderr.pipe(process.stderr);
const result = await promise;
return result.stdout.trim();
};

View File

@ -2,12 +2,27 @@ import execCommand from './execCommand';
import commandToString from './commandToString';
import splitCommandString from './splitCommandString';
import { dirname } from 'path';
import { pathExists } from 'fs-extra';
const rootDir = dirname(dirname(dirname(__dirname)));
let rootDir_ = '';
const getRootDir = async () => {
if (rootDir_) return rootDir_;
let p = dirname(dirname(dirname(__dirname)));
for (let i = 0; i < 9999; i++) {
if (await pathExists(`${p}/.eslintrc.js`)) {
rootDir_ = p;
return rootDir_;
}
p = dirname(p);
}
throw new Error('Could not find root dir');
};
export {
execCommand,
commandToString,
splitCommandString,
rootDir,
getRootDir,
};

View File

@ -20,6 +20,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"execa": "5.1.1",
"fs-extra": "11.1.1",
"node-fetch": "2.6.7"
},
"devDependencies": {

View File

@ -1,5 +1,20 @@
# Joplin Android app changelog
## [android-v2.11.2](https://github.com/laurent22/joplin/releases/tag/android-v2.11.2) (Pre-release) - 2023-04-09T12:04:06Z
- Improved: Resolve #8022: Editor syntax highlighting was broken (#8023) (#8022 by Henry Heino)
- Improved: Updated packages @react-native-community/netinfo (v9.3.8)
- Fixed: Removed `MasterKey` from Sync Status report (#8026) (#7940 by Arun Kumar)
- Security: Prevent bypassing fingerprint lock on certain devices (6b72f86)
## [android-v2.11.1](https://github.com/laurent22/joplin/releases/tag/android-v2.11.1) (Pre-release) - 2023-04-08T08:49:19Z
- New: Add log info for biometrics feature (efdbaeb)
- New: Add setting to enable/disable the markdown toolbar (#7929 by Henry Heino)
- Fixed: Encode the non-ASCII characters in OneDrive URI (#7868) (#7851 by Self Not Found)
- Fixed: Fix OneDrive sync attempting to call method on `null` variable (#7987) (#7986 by Henry Heino)
- Updated packages @lezer/highlight (v1.1.4), fs-extra (v11.1.1), jsdom (v21.1.1), markdown-it-multimd-table (v4.2.1), nanoid (v3.3.6), node-persist (v3.1.3), nodemon (v2.0.22), react-native-document-picker (v8.1.4), react-native-image-picker (v5.3.1), react-native-paper (v5.4.1), react-native-share (v8.2.1), sass (v1.59.3), sqlite3 (v5.1.6), turndown (v7.1.2), yargs (v17.7.1)
## [android-v2.10.9](https://github.com/laurent22/joplin/releases/tag/android-v2.10.9) (Pre-release) - 2023-03-22T18:40:57Z
- Improved: Mark biometrics feature as beta and ensure no call is made if it is not enabled (e44a934)

View File

@ -415,6 +415,30 @@
"created_at": "2023-03-30T18:21:27Z",
"repoId": 79162682,
"pullRequestNo": 7999
},
{
"name": "gitstart",
"id": 1501599,
"comment_id": 1499665006,
"created_at": "2023-04-06T21:46:22Z",
"repoId": 79162682,
"pullRequestNo": 8024
},
{
"name": "Letty",
"id": 465678,
"comment_id": 1500142614,
"created_at": "2023-04-07T10:04:51Z",
"repoId": 79162682,
"pullRequestNo": 8029
},
{
"name": "tbjers",
"id": 1117052,
"comment_id": 1501316440,
"created_at": "2023-04-10T02:33:42Z",
"repoId": 79162682,
"pullRequestNo": 8036
}
]
}

View File

@ -0,0 +1,19 @@
# Joplin Server items
To upload an item to Joplin Server:
- Call `PUT /api/items` with the serialized Joplin item. Examples of serialized items are described in `packages/app-cli/tests/support/syncTargetSnapshots`
- That route is in `packages/server/src/routes/api/items.ts`. In there it's going to do some basic processing on the item, and eventually will call `models.item().saveFromRawContent`
- This `saveFromRawContent` is where most of the job is done - it's going to detect what the item is, whether it's a note, notebook, etc. (this is the serialised content, as described above), or a binary file (resource).
- If it's a resource, the content is going to be saved as-is in the database
- If it's an item, it's going to deserialise it because we want to save certain properties separately in the database, such as the parent ID, the type (whether it's a note, notebook, etc.). We save these properties separately purely for performance reasons. Once the properties have been extracted, the rest of the object is serialised back to JSON and saved to the database.
- In the end, the content is saved to the `items` table. The JSON item or the resource binary content will be saved to the `content` field. Other Joplin items properties will be saved to the `jop_*` fields. For example, the ID, the parent ID, whether encryption is enabled, etc.
- `items.jop_id` is the ID as it was generated on the client. `items.id` is the server-side ID. We need two different IDs because we have no way to guarantee that `items.jop_id` is globally unique since it's generated client-side.
- In `ItemModel` there are various utility functions to deal with the content. This is because it may be saved in different places depending on configuration. It can be saved to the `items.content` field in the database, or it can be saved to S3, or to the filesystem. This is why any code that deals with item content must used these utility functions.

39
readme/spec/sync.md Normal file
View File

@ -0,0 +1,39 @@
# Joplin synchronisation
The Joplin applications are offline first - it means that data is saved locally on the device. In order to have the same data on all the user's devices, we use a synchronisation process. In a nutshell, each device uploads its notes, notebooks, tags, etc. to the server, and also downloads any notes they do not have, or any recent changes. If a note is deleted, it is also deleted from the server, and eventually deleted from each device too.
## Vocabulary
### Clients
The sync clients are the Joplin applications - the desktop, mobile and terminal applications.
### Sync targets
The sync target is the location where the data is going to be saved. It can be for example Joplin Server, a Nextcloud instance, or a WebDAV server.
### Items
The "items" are the notes, notebooks, tags and resources that need to be synced.
## General process
Whenever the user makes a change to an item, it is uploaded to the sync target within a few seconds. Uploading items as soon as possible helps limit conflicts. Because that way, any client that connects to the sync target is more likely to get the latest version of the item.
Additionally, every few minutes, the client is going to poll the server and download the latest changes, and apply them to the local note collection.
## Code architecture
- `packages/lib/Synchronizer.ts`: This file is responsible for the main synchronisation process. It download changes, upload them, and apply any deletion. The class is relatively generic and receive a `SyncTarget` object that handles sync target-specific operations. The synchroniser is also going encrypt and decrypt items if E2EE is enabled.
- `packages/lib/SyncTarget*.ts`: These files are the entry points for the various sync targets. They expose some metadata such as name, description, what options they support, etc. Some may also implement a function to test whether the configuration is working (used from the configuration screen). Finally, the main role of this class is to initialise an instance of a `FileApi`.
- `packages/lib/file-api-driver-*.ts`: Those are the file APIs. They must implement generic file-like operations to create, update, delete or list files. This API is in turn used by the synchroniser to created, update or delete items.
- `packages/lib/*Api.ts`: The `file-api-driver` will call some low-level API to perform its operations. For example `file-api-driver-local` will use the `fs` package to read/write files, `file-api-driver-amazon-s3` will use the AWS API to work with S3. In some cases however such a low-level API is not available - in that case, we usually create an `*Api.ts` file, which is used by the file API driver to perform its operations. For example, there is a `JoplinServerApi.ts`, which is used to connect to Joplin Server.
## See also
- [Synchronisation lock](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md)
- [E2EE: Technical spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/e2ee.md)
- [E2EE: Workflow](https://github.com/laurent22/joplin/blob/dev/readme/spec/e2ee/workflow.md)

View File

@ -4,13 +4,13 @@
"config:base"
],
"major": {
"stabilityDays": 30,
"stabilityDays": 80,
},
"minor": {
"stabilityDays": 20,
"stabilityDays": 40,
},
"patch": {
"stabilityDays": 10,
"stabilityDays": 20,
},
"prConcurrentLimit": 5,
"prHourlyLimit": 0,
@ -38,6 +38,7 @@
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/highlight",
"@fortawesome/fontawesome-svg-core",
"@fortawesome/free-solid-svg-icons",
"@svgr/webpack",

240
yarn.lock
View File

@ -3191,8 +3191,8 @@ __metadata:
linkType: hard
"@codemirror/autocomplete@npm:^6.0.0":
version: 6.4.0
resolution: "@codemirror/autocomplete@npm:6.4.0"
version: 6.4.2
resolution: "@codemirror/autocomplete@npm:6.4.2"
dependencies:
"@codemirror/language": ^6.0.0
"@codemirror/state": ^6.0.0
@ -3203,19 +3203,19 @@ __metadata:
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
checksum: 3470fee01da60d3d71b8b4f8728629c0f0441e704b8b828592f98c000d75fdb2c9077727e82685626cf45b95cadbc0c1a03968261df2f0cfb4162418b5f4dd1f
checksum: c6cc4edb1c412153e6f6f27926674d7f1d386d1f30d6d4f60c5b52bfa0105870b0c70449b69891937bcf082340d8b0fa6d1f9f28f5eb60adc2974ed4c73aadc1
languageName: node
linkType: hard
"@codemirror/commands@npm:6.1.2":
version: 6.1.2
resolution: "@codemirror/commands@npm:6.1.2"
"@codemirror/commands@npm:6.2.2":
version: 6.2.2
resolution: "@codemirror/commands@npm:6.2.2"
dependencies:
"@codemirror/language": ^6.0.0
"@codemirror/state": ^6.0.0
"@codemirror/state": ^6.2.0
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
checksum: 3e00b02ea12a5bb8a07162aea006e13e6dcbf9be8535e2fd645dd75634f39d8de2d18bb6ccf302fd22bd0956e38490ccf0bafc60d44945df19e449876b09a0df
checksum: d3aa1ca8cbd7b9434eedba6b6d783411670796bf6ab61990afc4fd0c04645189fe4dd55bb95e23b943e9089f9739bc7e92aa4b2ac3eac09cfa2b91a45f608d3e
languageName: node
linkType: hard
@ -3230,31 +3230,31 @@ __metadata:
linkType: hard
"@codemirror/lang-css@npm:^6.0.0":
version: 6.0.1
resolution: "@codemirror/lang-css@npm:6.0.1"
version: 6.1.1
resolution: "@codemirror/lang-css@npm:6.1.1"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/language": ^6.0.0
"@codemirror/state": ^6.0.0
"@lezer/css": ^1.0.0
checksum: c459baeeb912f424167cf8308df9fe20aaba3f6a65e5ce7b128e2d4d0ceda7474e3bf94b7b4b87ead7d10f79edfbf9807b2058a2354318adacf906eba4f6eb76
checksum: 9b0bf7c7544fb604b67325689d783981e4099560f577bc1f10c52cb18e9d275ebdbdbd3f335a1dbb9c4910c36320f74ca015fc92ef99f930ecb9d481a2bf3511
languageName: node
linkType: hard
"@codemirror/lang-html@npm:6.4.0, @codemirror/lang-html@npm:^6.0.0":
version: 6.4.0
resolution: "@codemirror/lang-html@npm:6.4.0"
"@codemirror/lang-html@npm:6.4.3, @codemirror/lang-html@npm:^6.0.0":
version: 6.4.3
resolution: "@codemirror/lang-html@npm:6.4.3"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/lang-css": ^6.0.0
"@codemirror/lang-javascript": ^6.0.0
"@codemirror/language": ^6.0.0
"@codemirror/language": ^6.4.0
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.2.2
"@lezer/common": ^1.0.0
"@lezer/css": ^1.1.0
"@lezer/html": ^1.1.0
checksum: 78fd811c6b2d2a355e1e41eaf599a9623dd03b8fd0ade603220dc443744520b06749125c85e5be64b09ca0aaea71de6268cf3518006ad17823522ef97aefab59
"@lezer/html": ^1.3.0
checksum: 6177d19147580964ecd6910ae951201929a96e63f4f0e624c3138e2805fa87ec6d6d952a3a888c5a52af78b6dd6d04d7d8c76c6a9cd65b1921dc467b5dbaea72
languageName: node
linkType: hard
@ -3268,39 +3268,24 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/lang-javascript@npm:6.1.1":
version: 6.1.1
resolution: "@codemirror/lang-javascript@npm:6.1.1"
"@codemirror/lang-javascript@npm:6.1.5, @codemirror/lang-javascript@npm:^6.0.0":
version: 6.1.5
resolution: "@codemirror/lang-javascript@npm:6.1.5"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/language": ^6.0.0
"@codemirror/language": ^6.6.0
"@codemirror/lint": ^6.0.0
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
"@lezer/javascript": ^1.0.0
checksum: 9a1d082edf3d835e18f2e9e0aa603c92a3e8e86f95a5662635f5a9d1ee6b87bbfe3bac3f56bd2c8cd46bafb87bcf90e0955c5efcda334a224c5d9c2d0524d351
checksum: f0355f9577fac03437137356b5c8826ec073480d9b0efc62289eac483172d47dafe569f31bf788e4228e8b789197e50a0768cf10b0cde5f600e89b6b469f52cc
languageName: node
linkType: hard
"@codemirror/lang-javascript@npm:^6.0.0":
version: 6.1.2
resolution: "@codemirror/lang-javascript@npm:6.1.2"
dependencies:
"@codemirror/autocomplete": ^6.0.0
"@codemirror/language": ^6.0.0
"@codemirror/lint": ^6.0.0
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
"@lezer/javascript": ^1.0.0
checksum: f4336b7efd44e4158b9979f0c23918184c897d0fe3e40b5414bd9243a9899ecdba4dfe13970fe5024a1894579af80cb4c5dd574c6c2b7bd7ff06d8c8cb88616b
languageName: node
linkType: hard
"@codemirror/lang-markdown@npm:6.0.5":
version: 6.0.5
resolution: "@codemirror/lang-markdown@npm:6.0.5"
"@codemirror/lang-markdown@npm:6.1.0":
version: 6.1.0
resolution: "@codemirror/lang-markdown@npm:6.1.0"
dependencies:
"@codemirror/lang-html": ^6.0.0
"@codemirror/language": ^6.3.0
@ -3308,7 +3293,7 @@ __metadata:
"@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0
"@lezer/markdown": ^1.0.0
checksum: d69148ea3f954aaae98cb22ef2ae790a1d51ad9a22344b2cf3e8b7cdb0e953c342f736257a88fb71b4b74f0fd4338480091fdb047e266b4c6665e4a83f552d4c
checksum: faee880c5e695391fc5b92788d1500bed3f0cc3766c987077cdc1643cf38b97eb1774a29491a7a75064089478b895e7c8fe5a4f08ac93c9614ccbbe188f10b47
languageName: node
linkType: hard
@ -3335,9 +3320,9 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/language@npm:6.3.2, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.3.0":
version: 6.3.2
resolution: "@codemirror/language@npm:6.3.2"
"@codemirror/language@npm:6.6.0, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.3.0, @codemirror/language@npm:^6.4.0, @codemirror/language@npm:^6.6.0":
version: 6.6.0
resolution: "@codemirror/language@npm:6.6.0"
dependencies:
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
@ -3345,56 +3330,56 @@ __metadata:
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
style-mod: ^4.0.0
checksum: b70ed9b85d0bea79181c86e88a1f5c0bada30680ee1fe6a68efc01bc037c3d14f94a83602fc46cc4b4393589605ef7e986ed5174443502f3365dd61f883894fa
checksum: bb9411620e2f231653a3f0c4429e0d19a3843bff5dbc117df4649d7bf783ec4ad809c0add8bc0887a4ec3f48b4f8f941621168e47d76101d5383f0d670af1722
languageName: node
linkType: hard
"@codemirror/legacy-modes@npm:6.3.1":
version: 6.3.1
resolution: "@codemirror/legacy-modes@npm:6.3.1"
"@codemirror/legacy-modes@npm:6.3.2":
version: 6.3.2
resolution: "@codemirror/legacy-modes@npm:6.3.2"
dependencies:
"@codemirror/language": ^6.0.0
checksum: 9065e521bf14e33856e9d3ea114d7b352adf341a8b8d4fb94b4c866189336a32b5ed42ffc20f5d2fa3c839f1bdf29a868bbf9b74c105ed83fa9fd6080e0429e9
checksum: fa5f5477fb9e19267251e2ecd3de8c1a4c2512813555bb60111dce3951f2c3f6080a2985a573b7542534ba1d2c34115f7e39ee23fdf8f6f81db6f8ce447c1efc
languageName: node
linkType: hard
"@codemirror/lint@npm:^6.0.0":
version: 6.1.0
resolution: "@codemirror/lint@npm:6.1.0"
version: 6.2.0
resolution: "@codemirror/lint@npm:6.2.0"
dependencies:
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
crelt: ^1.0.5
checksum: 1b5179c2c18e0eb96c68b47cd6832053a6aab80d6126cc250876fa848418c537417f0656cd6f442a0e5858830546fdef6bdcbb7b9773d92989795b52c7646011
checksum: b97e55a07bca9f7e357e495853ba189ae0ff7dfe7e7ae445d7a0d6c6926ec792c7f5c6b6c13a1f137fd9fedf44a6624e9d500f76d0d46a3c3e9d19c2cda9d28a
languageName: node
linkType: hard
"@codemirror/search@npm:6.2.3":
version: 6.2.3
resolution: "@codemirror/search@npm:6.2.3"
"@codemirror/search@npm:6.3.0":
version: 6.3.0
resolution: "@codemirror/search@npm:6.3.0"
dependencies:
"@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0
crelt: ^1.0.5
checksum: 7ab0ffab7992f5c6260313e06ec8935f55807b95ca86f0327154ea1ae0ab984cd22c2fc1a812bd6cace1db131785353689fbfd080d2e12c660e3db0295dec355
checksum: b757eebbb541c9d74fe36ccfdd03bc3e4e7aebb08b491e207d5898f24aaa612558c393ba49de5bf375972f5774de817fcfbad1ac551dda1a34badb41cf130d36
languageName: node
linkType: hard
"@codemirror/state@npm:6.1.4, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.4":
version: 6.1.4
resolution: "@codemirror/state@npm:6.1.4"
checksum: ef6bc495d3b89d2f0202b6b1c0fc3e3610f8d815f4ca91f86153d6093c9b93ddbe2cfb787802d07514a7114f0b200cf2eeb54c1cd34d7e00eff774ecea97d845
"@codemirror/state@npm:6.2.0, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.4, @codemirror/state@npm:^6.2.0":
version: 6.2.0
resolution: "@codemirror/state@npm:6.2.0"
checksum: fdc99c773dc09c700dd02bf918f06132aa8d3069c262cc4eb6ca5c810ce24ae2d7e90719ae7630a8158fd263018de6d40bd78f312e6bfba754e737b64e6c6b3d
languageName: node
linkType: hard
"@codemirror/view@npm:6.7.1, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.2.2, @codemirror/view@npm:^6.6.0":
version: 6.7.1
resolution: "@codemirror/view@npm:6.7.1"
"@codemirror/view@npm:6.9.3, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.2.2, @codemirror/view@npm:^6.6.0":
version: 6.9.3
resolution: "@codemirror/view@npm:6.9.3"
dependencies:
"@codemirror/state": ^6.1.4
style-mod: ^4.0.0
w3c-keyname: ^2.2.4
checksum: 75a5846d61e63027e9bf1dfd0b507932934cb7650b7959c1191e68b161eb1756e9773f964c4331970b51864aef8f7954bc5cc8fdb51b0f6533de6c20568833ed
checksum: 718ecbb021ca75eb89003f73c846a07d36a708dcfec8345f0f0dbcfc0d0df5ea6f114918694b2730a6d49e5e50502bcce79ce7ff94ce55748e068e5a35073755
languageName: node
linkType: hard
@ -4996,19 +4981,19 @@ __metadata:
dependencies:
"@babel/core": 7.16.0
"@babel/runtime": 7.16.3
"@codemirror/commands": 6.1.2
"@codemirror/commands": 6.2.2
"@codemirror/lang-cpp": 6.0.2
"@codemirror/lang-html": 6.4.0
"@codemirror/lang-html": 6.4.3
"@codemirror/lang-java": 6.0.1
"@codemirror/lang-javascript": 6.1.1
"@codemirror/lang-markdown": 6.0.5
"@codemirror/lang-javascript": 6.1.5
"@codemirror/lang-markdown": 6.1.0
"@codemirror/lang-php": 6.0.1
"@codemirror/lang-rust": 6.0.1
"@codemirror/language": 6.3.2
"@codemirror/legacy-modes": 6.3.1
"@codemirror/search": 6.2.3
"@codemirror/state": 6.1.4
"@codemirror/view": 6.7.1
"@codemirror/language": 6.6.0
"@codemirror/legacy-modes": 6.3.2
"@codemirror/search": 6.3.0
"@codemirror/state": 6.2.0
"@codemirror/view": 6.9.3
"@joplin/lib": ~2.11
"@joplin/react-native-alarm-notification": ~2.11
"@joplin/react-native-saf-x": ~2.11
@ -5018,7 +5003,7 @@ __metadata:
"@react-native-community/clipboard": 1.5.1
"@react-native-community/datetimepicker": 6.7.5
"@react-native-community/geolocation": 2.1.0
"@react-native-community/netinfo": 9.3.7
"@react-native-community/netinfo": 9.3.8
"@react-native-community/push-notification-ios": 1.10.1
"@react-native-community/slider": 4.4.2
"@types/fs-extra": 9.0.13
@ -5062,7 +5047,7 @@ __metadata:
react-native-image-picker: 5.3.1
react-native-image-resizer: 1.4.5
react-native-modal-datetime-picker: 14.0.1
react-native-paper: 5.4.1
react-native-paper: 5.5.0
react-native-popup-menu: 0.16.1
react-native-quick-actions: 0.3.13
react-native-rsa-native: 2.0.5
@ -5199,7 +5184,7 @@ __metadata:
moment: 2.29.4
multiparty: 4.2.3
mustache: 4.2.0
nanoid: 3.3.4
nanoid: 3.3.6
node-fetch: 2.6.7
node-notifier: 10.0.1
node-persist: 3.1.3
@ -5490,6 +5475,7 @@ __metadata:
"@types/jest": 29.5.0
"@types/node-fetch": 2.6.2
execa: 5.1.1
fs-extra: 11.1.1
jest: 29.5.0
node-fetch: 2.6.7
ts-jest: 29.0.5
@ -6388,12 +6374,12 @@ __metadata:
linkType: hard
"@lezer/cpp@npm:^1.0.0":
version: 1.0.0
resolution: "@lezer/cpp@npm:1.0.0"
version: 1.1.0
resolution: "@lezer/cpp@npm:1.1.0"
dependencies:
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
checksum: 6829550db06ea9ce149fbcd50db3c8988e69bb8e2ce557ecde8f711222b902c5c64453fa77c502c6ab13381d041f5f8e8d3cb80049b2e9e963d76294533e5e83
checksum: 9b25c881fc9b64fd2b019a077a85b0ba7cfda0bbdd92dbb0ff43300c9ba1ec4360128fe912bfe0f06a1c1bb5a564c5ace375c8aad254d07a717768a8f268695d
languageName: node
linkType: hard
@ -6407,7 +6393,7 @@ __metadata:
languageName: node
linkType: hard
"@lezer/highlight@npm:1.1.4":
"@lezer/highlight@npm:1.1.4, @lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3":
version: 1.1.4
resolution: "@lezer/highlight@npm:1.1.4"
dependencies:
@ -6416,52 +6402,43 @@ __metadata:
languageName: node
linkType: hard
"@lezer/highlight@npm:^1.0.0":
version: 1.1.3
resolution: "@lezer/highlight@npm:1.1.3"
dependencies:
"@lezer/common": ^1.0.0
checksum: 90ec143ce46b32f6779c3b245f1b5a540d66686939816d3daed8318821acc4bc719466dc222336cfd483bf04a8de4fdc6f279e904cf114d4d9f786f9feccbbd8
languageName: node
linkType: hard
"@lezer/html@npm:^1.1.0":
version: 1.3.0
resolution: "@lezer/html@npm:1.3.0"
"@lezer/html@npm:^1.3.0":
version: 1.3.4
resolution: "@lezer/html@npm:1.3.4"
dependencies:
"@lezer/common": ^1.0.0
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
checksum: e6efde94614a5b7ebf2713b244a110ef9025369561c7bf42fe6dd8f5877d2ee0c71f894b8b43d1284d23bf429fd3688ec3b6b0c2b8702df366c2b5e5cedc4c19
checksum: 81dd134ac094edf7c40bae4c3b7126d336ce4c3c87756344bf604eff64d89b06fcb55f91618a4622eb0dae6d6015722f5bab58e2252d86e81fca8c3ced1a0c4d
languageName: node
linkType: hard
"@lezer/java@npm:^1.0.0":
version: 1.0.0
resolution: "@lezer/java@npm:1.0.0"
version: 1.0.3
resolution: "@lezer/java@npm:1.0.3"
dependencies:
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
checksum: 0dcd3ea2aa431bc352ed1ca1e92c61a1d60e10d1c0e730200ef1f1dda4b42421e67d56e7808e2102f16b6ffd534f246b82249922998663e9099bd52c141ef1d9
checksum: 2fffea6627d130413ffad4e61040267974cca3167d98881b9e5b5e2455530de74a82c234d93603e92a4972fad314671453c49c0a76b0f4547c4617d671fd7b99
languageName: node
linkType: hard
"@lezer/javascript@npm:^1.0.0":
version: 1.4.0
resolution: "@lezer/javascript@npm:1.4.0"
version: 1.4.2
resolution: "@lezer/javascript@npm:1.4.2"
dependencies:
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
checksum: 36c64e8530feef9b937cf75f8833aa8c0f5c8c0812c55c53a133d1af5deb491dd80084397d5773e873db90ff717aede25b45fa827eead66400cb81b097567c42
"@lezer/highlight": ^1.1.3
"@lezer/lr": ^1.3.0
checksum: 542261c297709babfe450de1233c13fe2f5b111678d280cb0f8304f12bcdae294cb43c0ac64bbd647e5039de3286f6f0715d120fb132bd5af778363d1f612a1f
languageName: node
linkType: hard
"@lezer/lr@npm:^1.0.0":
version: 1.2.5
resolution: "@lezer/lr@npm:1.2.5"
"@lezer/lr@npm:^1.0.0, @lezer/lr@npm:^1.1.0, @lezer/lr@npm:^1.3.0":
version: 1.3.3
resolution: "@lezer/lr@npm:1.3.3"
dependencies:
"@lezer/common": ^1.0.0
checksum: 9a2fb2663dba5608c0f8a7d51b4c1beeb37d391da972fb3569fe51b637167ac4889b055ceb0c5267b8612a0aa5dfd517cbbd1349975cd662d1ca7fea374916b1
checksum: 1804074c794005a31c54d80ab72127f19ae5be29bb627c52bc001a57b1af97a9e62732ff13e3aeb7bc53b330202b6bd3747272c64d87f257dbba533e75a183a3
languageName: node
linkType: hard
@ -6476,12 +6453,12 @@ __metadata:
linkType: hard
"@lezer/php@npm:^1.0.0":
version: 1.0.0
resolution: "@lezer/php@npm:1.0.0"
version: 1.0.1
resolution: "@lezer/php@npm:1.0.1"
dependencies:
"@lezer/highlight": ^1.0.0
"@lezer/lr": ^1.0.0
checksum: 06d20c423011119363ccd4dd30d0bdec56ddbdddda05888a6b5890fc090a6338740a310a77d367d3d69a958925fad73e0c7f9b62953eb3f189ec513bd71d9f59
"@lezer/lr": ^1.1.0
checksum: a847c255c030b4d38913ddf1d5bd7324d83be7ef8d1d244542870be03b9bf7dc71283afeb2415c40dfd188cb99f0cc44bad760b5f3b7c35c3b8e5e00253848fc
languageName: node
linkType: hard
@ -7082,12 +7059,12 @@ __metadata:
languageName: node
linkType: hard
"@react-native-community/netinfo@npm:9.3.7":
version: 9.3.7
resolution: "@react-native-community/netinfo@npm:9.3.7"
"@react-native-community/netinfo@npm:9.3.8":
version: 9.3.8
resolution: "@react-native-community/netinfo@npm:9.3.8"
peerDependencies:
react-native: ">=0.59"
checksum: 556e47312be4b3d74c4c8fa7ab0b526a9d8395745c4d94d813cd8f08284d2a16ba274d8a8fdc38d5a273c913ebbc356bc15e65a8c8ab4506f7612a08e43da83d
checksum: 1b41c2960211716189da94bf9a8070d28c34935dc914d1094d5a9de7b7137af093bd0e71f82fc158b973d9b5b13a7685ecca6430ad23d110b23129157907e102
languageName: node
linkType: hard
@ -17108,7 +17085,7 @@ __metadata:
dependencies:
chalk: 2.4.2
jest: 29.4.3
slugify: 1.6.5
slugify: 1.6.6
yeoman-generator: 5.8.0
yosay: 2.0.2
languageName: unknown
@ -24021,12 +23998,12 @@ __metadata:
languageName: node
linkType: hard
"nanoid@npm:3.3.4, nanoid@npm:^3.3.4":
version: 3.3.4
resolution: "nanoid@npm:3.3.4"
"nanoid@npm:3.3.6":
version: 3.3.6
resolution: "nanoid@npm:3.3.6"
bin:
nanoid: bin/nanoid.cjs
checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c
checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3
languageName: node
linkType: hard
@ -24039,6 +24016,15 @@ __metadata:
languageName: node
linkType: hard
"nanoid@npm:^3.3.4":
version: 3.3.4
resolution: "nanoid@npm:3.3.4"
bin:
nanoid: bin/nanoid.cjs
checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c
languageName: node
linkType: hard
"nanomatch@npm:^1.2.9":
version: 1.2.13
resolution: "nanomatch@npm:1.2.13"
@ -27375,9 +27361,9 @@ __metadata:
languageName: node
linkType: hard
"react-native-paper@npm:5.4.1":
version: 5.4.1
resolution: "react-native-paper@npm:5.4.1"
"react-native-paper@npm:5.5.0":
version: 5.5.0
resolution: "react-native-paper@npm:5.5.0"
dependencies:
"@callstack/react-theme-provider": ^3.0.8
color: ^3.1.2
@ -27387,7 +27373,7 @@ __metadata:
react-native: "*"
react-native-safe-area-context: "*"
react-native-vector-icons: "*"
checksum: b6bca70b935ca3b69ba4d2bc16717b938b4219d0a1fc17b1e0c7b87df753ba3cd8f52e8f17cf09ca862c7e6be243fb52092054fc176a4b284fa3143cf32d0672
checksum: 1d433e5d48462a8a6477d979f7c9ae44278dfaf713d5f57ff218cfbd8d6f6556d42ace6d01cce410a922157242bd61f92bdf87e0cbd84c6231f1ab369af1777b
languageName: node
linkType: hard
@ -28809,6 +28795,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "root@workspace:."
dependencies:
"@joplin/utils": ~2.11
"@seiyab/eslint-plugin-react-hooks": 4.5.1-beta.0
"@types/fs-extra": 9.0.13
"@typescript-eslint/eslint-plugin": 5.48.2
@ -28820,6 +28807,7 @@ __metadata:
eslint-plugin-jest: 27.2.1
eslint-plugin-promise: 6.1.1
eslint-plugin-react: 7.32.0
execa: 5.1.1
fs-extra: 11.1.1
glob: 8.1.0
gulp: 4.0.2
@ -29634,10 +29622,10 @@ __metadata:
languageName: node
linkType: hard
"slugify@npm:1.6.5":
version: 1.6.5
resolution: "slugify@npm:1.6.5"
checksum: a955a1b600201030f4c1daa9bb74a17d4402a0693fc40978bbd17e44e64fd72dad3bac4037422aa8aed55b5170edd57f3f4cd8f59ba331f5cf0f10f1a7795609
"slugify@npm:1.6.6":
version: 1.6.6
resolution: "slugify@npm:1.6.6"
checksum: 04773c2d3b7aea8d2a61fa47cc7e5d29ce04e1a96cbaec409da57139df906acb3a449fac30b167d203212c806e73690abd4ff94fbad0a9a7b7ea109a2a638ae9
languageName: node
linkType: hard