You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-14 23:26:58 +02:00
Compare commits
45 Commits
android-v2
...
mac-binary
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
357277cda0 | ||
|
|
8c1568ae81 | ||
|
|
66e479d13d | ||
|
|
700c1410fa | ||
|
|
3873e15a21 | ||
|
|
72ec2d3b1e | ||
|
|
7eddbc7002 | ||
|
|
d593ed130e | ||
|
|
b2eabb3836 | ||
|
|
5e1c789926 | ||
|
|
4f552b396c | ||
|
|
132bcbd477 | ||
|
|
8f6192464b | ||
|
|
897addec4e | ||
|
|
f3eea43d24 | ||
|
|
8babaddbcb | ||
|
|
13cdaabb17 | ||
|
|
a94aa21088 | ||
|
|
6116bed4e3 | ||
|
|
fabd0b4dda | ||
|
|
6b72f86e7b | ||
|
|
02cf546124 | ||
|
|
eecb012d64 | ||
|
|
51e865c467 | ||
|
|
ac8b3ae0cc | ||
|
|
0b3919fd62 | ||
|
|
db69125e8f | ||
|
|
ccabc778f6 | ||
|
|
56b9c9fbc9 | ||
|
|
c68d196baa | ||
|
|
897fd0f727 | ||
|
|
9edb402b18 | ||
|
|
36ae58ffe1 | ||
|
|
5ef8b86957 | ||
|
|
c7228cfcd6 | ||
|
|
7a791dbf98 | ||
|
|
a543588527 | ||
|
|
998ad142a6 | ||
|
|
b6a53f96f7 | ||
|
|
841a9c6e09 | ||
|
|
d25c078ef0 | ||
|
|
ff69ac17be | ||
|
|
b44945b3a0 | ||
|
|
2584224026 | ||
|
|
46136871bf |
@@ -330,6 +330,7 @@ packages/app-desktop/gui/style/StyledTextInput.js
|
|||||||
packages/app-desktop/gui/utils/NoteListUtils.js
|
packages/app-desktop/gui/utils/NoteListUtils.js
|
||||||
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
||||||
packages/app-desktop/gui/utils/loadScript.js
|
packages/app-desktop/gui/utils/loadScript.js
|
||||||
|
packages/app-desktop/gulpfile.js
|
||||||
packages/app-desktop/plugins/GotoAnything.js
|
packages/app-desktop/plugins/GotoAnything.js
|
||||||
packages/app-desktop/services/bridge.js
|
packages/app-desktop/services/bridge.js
|
||||||
packages/app-desktop/services/commands/stateToWhenClauseContext.js
|
packages/app-desktop/services/commands/stateToWhenClauseContext.js
|
||||||
@@ -405,6 +406,7 @@ packages/app-mobile/components/SideMenu.js
|
|||||||
packages/app-mobile/components/TextInput.js
|
packages/app-mobile/components/TextInput.js
|
||||||
packages/app-mobile/components/app-nav.js
|
packages/app-mobile/components/app-nav.js
|
||||||
packages/app-mobile/components/biometrics/BiometricPopup.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/biometrics/sensorInfo.js
|
||||||
packages/app-mobile/components/getResponsiveValue.js
|
packages/app-mobile/components/getResponsiveValue.js
|
||||||
packages/app-mobile/components/getResponsiveValue.test.js
|
packages/app-mobile/components/getResponsiveValue.test.js
|
||||||
|
|||||||
3
.github/scripts/run_ci.sh
vendored
3
.github/scripts/run_ci.sh
vendored
@@ -180,9 +180,6 @@ cd "$ROOT_DIR/packages/app-desktop"
|
|||||||
|
|
||||||
if [[ $GIT_TAG_NAME = v* ]]; then
|
if [[ $GIT_TAG_NAME = v* ]]; then
|
||||||
echo "Step: Building and publishing desktop application..."
|
echo "Step: Building and publishing desktop application..."
|
||||||
# cd "$ROOT_DIR/packages/tools"
|
|
||||||
# node bundleDefaultPlugins.js
|
|
||||||
cd "$ROOT_DIR/packages/app-desktop"
|
|
||||||
USE_HARD_LINKS=false yarn run dist
|
USE_HARD_LINKS=false yarn run dist
|
||||||
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
|
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
|
||||||
echo "Step: Building Docker Image..."
|
echo "Step: Building Docker Image..."
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -317,6 +317,7 @@ packages/app-desktop/gui/style/StyledTextInput.js
|
|||||||
packages/app-desktop/gui/utils/NoteListUtils.js
|
packages/app-desktop/gui/utils/NoteListUtils.js
|
||||||
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
||||||
packages/app-desktop/gui/utils/loadScript.js
|
packages/app-desktop/gui/utils/loadScript.js
|
||||||
|
packages/app-desktop/gulpfile.js
|
||||||
packages/app-desktop/plugins/GotoAnything.js
|
packages/app-desktop/plugins/GotoAnything.js
|
||||||
packages/app-desktop/services/bridge.js
|
packages/app-desktop/services/bridge.js
|
||||||
packages/app-desktop/services/commands/stateToWhenClauseContext.js
|
packages/app-desktop/services/commands/stateToWhenClauseContext.js
|
||||||
@@ -392,6 +393,7 @@ packages/app-mobile/components/SideMenu.js
|
|||||||
packages/app-mobile/components/TextInput.js
|
packages/app-mobile/components/TextInput.js
|
||||||
packages/app-mobile/components/app-nav.js
|
packages/app-mobile/components/app-nav.js
|
||||||
packages/app-mobile/components/biometrics/BiometricPopup.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/biometrics/sensorInfo.js
|
||||||
packages/app-mobile/components/getResponsiveValue.js
|
packages/app-mobile/components/getResponsiveValue.js
|
||||||
packages/app-mobile/components/getResponsiveValue.test.js
|
packages/app-mobile/components/getResponsiveValue.test.js
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
|
"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",
|
"buildSequential": "yarn run tsc && yarn workspaces foreach --verbose --interlaced --topological-dev run build",
|
||||||
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
|
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
|
||||||
"buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js",
|
"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/",
|
"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/",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const gulp = require('gulp');
|
|||||||
const utils = require('@joplin/tools/gulp/utils');
|
const utils = require('@joplin/tools/gulp/utils');
|
||||||
const compileSass = require('@joplin/tools/compileSass');
|
const compileSass = require('@joplin/tools/compileSass');
|
||||||
const compilePackageInfo = require('@joplin/tools/compilePackageInfo');
|
const compilePackageInfo = require('@joplin/tools/compilePackageInfo');
|
||||||
|
const bundleDefaultPlugins = require('@joplin/tools/bundleDefaultPlugins');
|
||||||
|
|
||||||
const tasks = {
|
const tasks = {
|
||||||
compileScripts: {
|
compileScripts: {
|
||||||
@@ -35,6 +36,11 @@ const tasks = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
bundleDefaultPlugins: {
|
||||||
|
fn: async () => {
|
||||||
|
await bundleDefaultPlugins();
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.registerGulpTasks(gulp, tasks);
|
utils.registerGulpTasks(gulp, tasks);
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dist": "yarn run electronRebuild && npx electron-builder",
|
"dist": "yarn run bundleDefaultPlugins && yarn run electronRebuild && npx electron-builder",
|
||||||
|
"bundleDefaultPlugins": "gulp bundleDefaultPlugins",
|
||||||
"build": "gulp build",
|
"build": "gulp build",
|
||||||
"postinstall": "yarn run build",
|
"postinstall": "yarn run build",
|
||||||
"electronBuilder": "gulp electronBuilder",
|
"electronBuilder": "gulp electronBuilder",
|
||||||
@@ -90,7 +91,10 @@
|
|||||||
"CFBundleURLName": "org.joplinapp.x-callback-url"
|
"CFBundleURLName": "org.joplinapp.x-callback-url"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"binaries": [
|
||||||
|
"Contents/Resources/build/defaultPlugins/io.github.jackgruber.backup/plugin.jpl"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"icon": "../../Assets/LinuxIcons",
|
"icon": "../../Assets/LinuxIcons",
|
||||||
@@ -107,6 +111,8 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/laurent22/joplin#readme",
|
"homepage": "https://github.com/laurent22/joplin#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@electron/notarize": "1.2.3",
|
||||||
|
"@electron/rebuild": "3.2.10",
|
||||||
"@joplin/tools": "~2.11",
|
"@joplin/tools": "~2.11",
|
||||||
"@testing-library/react-hooks": "8.0.1",
|
"@testing-library/react-hooks": "8.0.1",
|
||||||
"@types/jest": "29.2.6",
|
"@types/jest": "29.2.6",
|
||||||
@@ -115,9 +121,7 @@
|
|||||||
"@types/react-redux": "7.1.25",
|
"@types/react-redux": "7.1.25",
|
||||||
"@types/styled-components": "5.1.26",
|
"@types/styled-components": "5.1.26",
|
||||||
"electron": "19.1.4",
|
"electron": "19.1.4",
|
||||||
"electron-builder": "23.6.0",
|
"electron-builder": "24.1.2",
|
||||||
"electron-notarize": "1.2.2",
|
|
||||||
"electron-rebuild": "3.2.9",
|
|
||||||
"glob": "8.1.0",
|
"glob": "8.1.0",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"jest": "29.4.3",
|
"jest": "29.4.3",
|
||||||
@@ -125,6 +129,7 @@
|
|||||||
"js-sha512": "0.8.0",
|
"js-sha512": "0.8.0",
|
||||||
"nan": "2.17.0",
|
"nan": "2.17.0",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "4.9.4"
|
"typescript": "4.9.4"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const electron_notarize = require('electron-notarize');
|
const electron_notarize = require('@electron/notarize');
|
||||||
const execCommand = require('./execCommand');
|
const execCommand = require('./execCommand');
|
||||||
|
|
||||||
function isDesktopAppTag(tagName) {
|
function isDesktopAppTag(tagName) {
|
||||||
|
|||||||
@@ -7,5 +7,12 @@
|
|||||||
"exclude": [
|
"exclude": [
|
||||||
"**/node_modules",
|
"**/node_modules",
|
||||||
"**/dist",
|
"**/dist",
|
||||||
|
"gulpfile.ts",
|
||||||
],
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": [
|
||||||
|
"jest",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -150,8 +150,8 @@ android {
|
|||||||
applicationId "net.cozic.joplin"
|
applicationId "net.cozic.joplin"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 2097686
|
versionCode 2097687
|
||||||
versionName "2.11.1"
|
versionName "2.11.2"
|
||||||
// ndk {
|
// ndk {
|
||||||
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -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:
|
import { expect, describe, it } from '@jest/globals';
|
||||||
//
|
|
||||||
// > 42 | expect(headerLineContent.textContent).toBe(headerLineText);
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
|
const createEditorSettings = (themeId: number) => {
|
||||||
|
const themeData = themeStyle(themeId);
|
||||||
|
const editorSettings: EditorSettings = {
|
||||||
|
katexEnabled: true,
|
||||||
|
spellcheckEnabled: true,
|
||||||
|
themeId,
|
||||||
|
themeData,
|
||||||
|
};
|
||||||
|
|
||||||
|
return editorSettings;
|
||||||
|
};
|
||||||
|
|
||||||
describe('CodeMirror', () => {
|
describe('CodeMirror', () => {
|
||||||
it('should succeed', async () => {
|
// This checks for a regression -- occasionally, when updating packages,
|
||||||
expect(1).toBe(1);
|
// 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');
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ const React = require('react');
|
|||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { View, Dimensions, Alert, Button } from 'react-native';
|
import { View, Dimensions, Alert, Button } from 'react-native';
|
||||||
import FingerprintScanner from 'react-native-fingerprint-scanner';
|
|
||||||
import { SensorInfo } from './sensorInfo';
|
import { SensorInfo } from './sensorInfo';
|
||||||
import { _ } from '@joplin/lib/locale';
|
import { _ } from '@joplin/lib/locale';
|
||||||
import Logger from '@joplin/lib/Logger';
|
import Logger from '@joplin/lib/Logger';
|
||||||
|
import biometricAuthenticate from './biometricAuthenticate';
|
||||||
|
|
||||||
const logger = Logger.create('BiometricPopup');
|
const logger = Logger.create('BiometricPopup');
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export default (props: Props) => {
|
|||||||
// doesn't work properly, we disable it. We only want the user to enable the
|
// 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.
|
// feature after they've read the description in the config screen.
|
||||||
const [initialPromptDone, setInitialPromptDone] = useState(true); // useState(Setting.value('security.biometricsInitialPromptDone'));
|
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);
|
const [tryBiometricsCheck, setTryBiometricsCheck] = useState(initialPromptDone);
|
||||||
|
|
||||||
logger.info('Render start');
|
logger.info('Render start');
|
||||||
@@ -37,18 +37,14 @@ export default (props: Props) => {
|
|||||||
logger.info('biometricsCheck: start');
|
logger.info('biometricsCheck: start');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info('biometricsCheck: authenticate...');
|
await biometricAuthenticate();
|
||||||
await FingerprintScanner.authenticate({ description: _('Verify your identity') });
|
|
||||||
logger.info('biometricsCheck: authenticate done');
|
|
||||||
setTryBiometricsCheck(false);
|
|
||||||
setDisplay(false);
|
setDisplay(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert(_('Could not verify your identify'), error.message);
|
Alert.alert(error.message);
|
||||||
setTryBiometricsCheck(false);
|
|
||||||
} finally {
|
|
||||||
FingerprintScanner.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTryBiometricsCheck(false);
|
||||||
|
|
||||||
logger.info('biometricsCheck: end');
|
logger.info('biometricsCheck: end');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +93,7 @@ export default (props: Props) => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}, [initialPromptDone, props.sensorInfo.supportedSensors, display, props.dispatch]);
|
}, [initialPromptDone, display, props.dispatch]);
|
||||||
|
|
||||||
const windowSize = useMemo(() => {
|
const windowSize = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -30,6 +30,18 @@ export default async (): Promise<SensorInfo> => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info('Getting isSensorAvailable...');
|
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();
|
const result = await FingerprintScanner.isSensorAvailable();
|
||||||
logger.info('isSensorAvailable result', result);
|
logger.info('isSensorAvailable result', result);
|
||||||
supportedSensors = result;
|
supportedSensors = result;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const { themeStyle } = require('../global-style.js');
|
|||||||
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||||
import { openDocumentTree } from '@joplin/react-native-saf-x';
|
import { openDocumentTree } from '@joplin/react-native-saf-x';
|
||||||
|
import biometricAuthenticate from '../biometrics/biometricAuthenticate';
|
||||||
|
|
||||||
class ConfigScreenComponent extends BaseScreenComponent {
|
class ConfigScreenComponent extends BaseScreenComponent {
|
||||||
public static navigationOptions(): any {
|
public static navigationOptions(): any {
|
||||||
@@ -463,7 +464,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
<Text key="label" style={this.styles().switchSettingText}>
|
<Text key="label" style={this.styles().switchSettingText}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</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>
|
</View>
|
||||||
{descriptionComp}
|
{descriptionComp}
|
||||||
</View>
|
</View>
|
||||||
@@ -474,13 +475,39 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
return !hasDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder;
|
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) {
|
public settingToComponent(key: string, value: any) {
|
||||||
const themeId = this.props.themeId;
|
const themeId = this.props.themeId;
|
||||||
const theme = themeStyle(themeId);
|
const theme = themeStyle(themeId);
|
||||||
const output: any = null;
|
const output: any = null;
|
||||||
|
|
||||||
const updateSettingValue = (key: string, value: any) => {
|
const updateSettingValue = async (key: string, value: any) => {
|
||||||
return shared.updateSettingValue(this, key, value);
|
const handled = await this.handleSetting(key, value);
|
||||||
|
if (!handled) shared.updateSettingValue(this, key, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const md = Setting.settingMetadata(key);
|
const md = Setting.settingMetadata(key);
|
||||||
@@ -517,7 +544,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
}}
|
}}
|
||||||
onValueChange={(itemValue: string) => {
|
onValueChange={(itemValue: string) => {
|
||||||
updateSettingValue(key, itemValue);
|
void updateSettingValue(key, itemValue);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -553,7 +580,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
||||||
<Text style={this.styles().sliderUnits}>{unitLabel}</Text>
|
<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>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -577,7 +604,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
<Text key="label" style={this.styles().settingText}>
|
<Text key="label" style={this.styles().settingText}>
|
||||||
{md.label()}
|
{md.label()}
|
||||||
</Text>
|
</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>
|
</View>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- react-native-image-resizer (1.4.5):
|
- react-native-image-resizer (1.4.5):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-netinfo (9.3.7):
|
- react-native-netinfo (9.3.8):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-rsa-native (2.0.5):
|
- react-native-rsa-native (2.0.5):
|
||||||
- React
|
- React
|
||||||
@@ -722,7 +722,7 @@ SPEC CHECKSUMS:
|
|||||||
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
|
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
|
||||||
react-native-image-picker: ec9b713e248760bfa0f879f0715391de4651a7cb
|
react-native-image-picker: ec9b713e248760bfa0f879f0715391de4651a7cb
|
||||||
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
|
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
|
||||||
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
|
react-native-netinfo: fbc23bc2fe217155d85f2f7e0644b1654df8029b
|
||||||
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
||||||
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
|
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
|
||||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"@react-native-community/clipboard": "1.5.1",
|
"@react-native-community/clipboard": "1.5.1",
|
||||||
"@react-native-community/datetimepicker": "6.7.5",
|
"@react-native-community/datetimepicker": "6.7.5",
|
||||||
"@react-native-community/geolocation": "2.1.0",
|
"@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/push-notification-ios": "1.10.1",
|
||||||
"@react-native-community/slider": "4.4.2",
|
"@react-native-community/slider": "4.4.2",
|
||||||
"assert-browserify": "2.0.0",
|
"assert-browserify": "2.0.0",
|
||||||
@@ -79,19 +79,19 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.16.0",
|
"@babel/core": "7.16.0",
|
||||||
"@babel/runtime": "7.16.3",
|
"@babel/runtime": "7.16.3",
|
||||||
"@codemirror/commands": "6.1.2",
|
"@codemirror/commands": "6.2.2",
|
||||||
"@codemirror/lang-cpp": "6.0.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-java": "6.0.1",
|
||||||
"@codemirror/lang-javascript": "6.1.1",
|
"@codemirror/lang-javascript": "6.1.5",
|
||||||
"@codemirror/lang-markdown": "6.0.5",
|
"@codemirror/lang-markdown": "6.1.0",
|
||||||
"@codemirror/lang-php": "6.0.1",
|
"@codemirror/lang-php": "6.0.1",
|
||||||
"@codemirror/lang-rust": "6.0.1",
|
"@codemirror/lang-rust": "6.0.1",
|
||||||
"@codemirror/language": "6.3.2",
|
"@codemirror/language": "6.6.0",
|
||||||
"@codemirror/legacy-modes": "6.3.1",
|
"@codemirror/legacy-modes": "6.3.2",
|
||||||
"@codemirror/search": "6.2.3",
|
"@codemirror/search": "6.3.0",
|
||||||
"@codemirror/state": "6.1.4",
|
"@codemirror/state": "6.2.0",
|
||||||
"@codemirror/view": "6.7.1",
|
"@codemirror/view": "6.9.3",
|
||||||
"@joplin/tools": "~2.11",
|
"@joplin/tools": "~2.11",
|
||||||
"@lezer/highlight": "1.1.4",
|
"@lezer/highlight": "1.1.4",
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ async function initialize(dispatch: Function) {
|
|||||||
if (Setting.value('env') === 'prod') {
|
if (Setting.value('env') === 'prod') {
|
||||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
|
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
|
||||||
} else {
|
} else {
|
||||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-1') });
|
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-3') });
|
||||||
|
|
||||||
// await db.clearForTesting();
|
// await db.clearForTesting();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"slugify": "1.6.5",
|
"slugify": "1.6.6",
|
||||||
"yeoman-generator": "5.8.0",
|
"yeoman-generator": "5.8.0",
|
||||||
"yosay": "2.0.2"
|
"yosay": "2.0.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ export default class ReportService {
|
|||||||
let syncedCount = 0;
|
let syncedCount = 0;
|
||||||
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
|
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {
|
||||||
const d = BaseItem.syncItemDefinitions_[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 ItemClass = BaseItem.getClass(d.className);
|
||||||
const o = {
|
const o = {
|
||||||
total: await ItemClass.count(),
|
total: await ItemClass.count(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { join } from 'path';
|
import { join, normalize } from 'path';
|
||||||
import { pathExists, mkdir, readFile, move, remove, writeFile } from 'fs-extra';
|
import { pathExists, mkdir, readFile, move, remove, writeFile } from 'fs-extra';
|
||||||
import { DefaultPluginsInfo } from '@joplin/lib/services/plugins/PluginService';
|
import { DefaultPluginsInfo } from '@joplin/lib/services/plugins/PluginService';
|
||||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||||
@@ -41,7 +41,7 @@ async function downloadFile(url: string, outputPath: string) {
|
|||||||
|
|
||||||
export async function extractPlugins(currentDir: string, defaultPluginDir: string, downloadedPluginsNames: PluginIdAndName): Promise<void> {
|
export async function extractPlugins(currentDir: string, defaultPluginDir: string, downloadedPluginsNames: PluginIdAndName): Promise<void> {
|
||||||
for (const pluginId of Object.keys(downloadedPluginsNames)) {
|
for (const pluginId of Object.keys(downloadedPluginsNames)) {
|
||||||
await execCommand(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: true });
|
await execCommand(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: true, splitCommandOptions: { handleEscape: false } });
|
||||||
await move(`package/publish/${pluginId}.jpl`, `${defaultPluginDir}/${pluginId}/plugin.jpl`, { overwrite: true });
|
await move(`package/publish/${pluginId}.jpl`, `${defaultPluginDir}/${pluginId}/plugin.jpl`, { overwrite: true });
|
||||||
await move(`package/publish/${pluginId}.json`, `${defaultPluginDir}/${pluginId}/manifest.json`, { overwrite: true });
|
await move(`package/publish/${pluginId}.json`, `${defaultPluginDir}/${pluginId}/manifest.json`, { overwrite: true });
|
||||||
await remove(`${downloadedPluginsNames[pluginId]}`);
|
await remove(`${downloadedPluginsNames[pluginId]}`);
|
||||||
@@ -74,7 +74,10 @@ export const downloadPlugins = async (localPluginsVersions: PluginAndVersion, de
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function start(): Promise<void> {
|
async function start(): Promise<void> {
|
||||||
const defaultPluginDir = join(__dirname, '..', '..', 'packages', 'app-desktop', 'build', 'defaultPlugins');
|
// windows CI fix: normalizing __dirname for windows
|
||||||
|
const cwd = normalize(__dirname);
|
||||||
|
process.chdir(cwd);
|
||||||
|
const defaultPluginDir = join(cwd, '..', '..', 'packages', 'app-desktop', 'build', 'defaultPlugins');
|
||||||
const defaultPluginsInfo = getDefaultPluginsInfo();
|
const defaultPluginsInfo = getDefaultPluginsInfo();
|
||||||
|
|
||||||
const manifestData = await fetch('https://raw.githubusercontent.com/joplin/plugins/master/manifests.json');
|
const manifestData = await fetch('https://raw.githubusercontent.com/joplin/plugins/master/manifests.json');
|
||||||
@@ -83,7 +86,7 @@ async function start(): Promise<void> {
|
|||||||
|
|
||||||
const localPluginsVersions = await localPluginsVersion(defaultPluginDir, defaultPluginsInfo);
|
const localPluginsVersions = await localPluginsVersion(defaultPluginDir, defaultPluginsInfo);
|
||||||
const downloadedPluginNames: PluginIdAndName = await downloadPlugins(localPluginsVersions, defaultPluginsInfo, manifests);
|
const downloadedPluginNames: PluginIdAndName = await downloadPlugins(localPluginsVersions, defaultPluginsInfo, manifests);
|
||||||
await extractPlugins(__dirname, defaultPluginDir, downloadedPluginNames);
|
await extractPlugins(cwd, defaultPluginDir, downloadedPluginNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
@@ -94,3 +97,7 @@ if (require.main === module) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
await start();
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as execa from 'execa';
|
import * as execa from 'execa';
|
||||||
import commandToString from './commandToString';
|
import commandToString from './commandToString';
|
||||||
import splitCommandString from './splitCommandString';
|
import splitCommandString, { SplitCommandOptions } from './splitCommandString';
|
||||||
import { stdout } from 'process';
|
import { stdout } from 'process';
|
||||||
|
|
||||||
interface ExecCommandOptions {
|
interface ExecCommandOptions {
|
||||||
@@ -8,6 +8,7 @@ interface ExecCommandOptions {
|
|||||||
showStdout?: boolean;
|
showStdout?: boolean;
|
||||||
showStderr?: boolean;
|
showStderr?: boolean;
|
||||||
quiet?: boolean;
|
quiet?: boolean;
|
||||||
|
splitCommandOptions?: SplitCommandOptions | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async (command: string | string[], options: ExecCommandOptions | null = null): Promise<string> => {
|
export default async (command: string | string[], options: ExecCommandOptions | null = null): Promise<string> => {
|
||||||
@@ -33,7 +34,7 @@ export default async (command: string | string[], options: ExecCommandOptions |
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
|
const args: string[] = typeof command === 'string' ? splitCommandString(command, options.splitCommandOptions || null) : command as string[];
|
||||||
const executableName = args[0];
|
const executableName = args[0];
|
||||||
args.splice(0, 1);
|
args.splice(0, 1);
|
||||||
const promise = execa(executableName, args);
|
const promise = execa(executableName, args);
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
export interface SplitCommandOptions {
|
||||||
|
handleEscape?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export default (command: string, options: any = null) => {
|
export default (command: string, options: any = null) => {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (!('handleEscape' in options)) {
|
if (!('handleEscape' in options)) {
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# Joplin Android app changelog
|
# 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
|
## [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 log info for biometrics feature (efdbaeb)
|
||||||
|
|||||||
@@ -431,6 +431,14 @@
|
|||||||
"created_at": "2023-04-07T10:04:51Z",
|
"created_at": "2023-04-07T10:04:51Z",
|
||||||
"repoId": 79162682,
|
"repoId": 79162682,
|
||||||
"pullRequestNo": 8029
|
"pullRequestNo": 8029
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tbjers",
|
||||||
|
"id": 1117052,
|
||||||
|
"comment_id": 1501316440,
|
||||||
|
"created_at": "2023-04-10T02:33:42Z",
|
||||||
|
"repoId": 79162682,
|
||||||
|
"pullRequestNo": 8036
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
"@codemirror/search",
|
"@codemirror/search",
|
||||||
"@codemirror/state",
|
"@codemirror/state",
|
||||||
"@codemirror/view",
|
"@codemirror/view",
|
||||||
|
"@lezer/highlight",
|
||||||
"@fortawesome/fontawesome-svg-core",
|
"@fortawesome/fontawesome-svg-core",
|
||||||
"@fortawesome/free-solid-svg-icons",
|
"@fortawesome/free-solid-svg-icons",
|
||||||
"@svgr/webpack",
|
"@svgr/webpack",
|
||||||
|
|||||||
Reference in New Issue
Block a user