You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-27 20:29:45 +02:00
Compare commits
2 Commits
test-binar
...
masterkey-
Author | SHA1 | Date | |
---|---|---|---|
|
e188c1c0c4 | ||
|
961156e22b |
@@ -405,7 +405,6 @@ 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
|
||||
|
3
.github/scripts/run_ci.sh
vendored
3
.github/scripts/run_ci.sh
vendored
@@ -180,6 +180,9 @@ cd "$ROOT_DIR/packages/app-desktop"
|
||||
|
||||
if [[ $GIT_TAG_NAME = v* ]]; then
|
||||
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
|
||||
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
|
||||
echo "Step: Building Docker Image..."
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -392,7 +392,6 @@ 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
|
||||
|
@@ -126,7 +126,6 @@ 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)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sz_program=${SZA_PATH:-7za}
|
||||
sz_type=${SZA_ARCHIVE_TYPE:-xz}
|
||||
|
||||
case $1 in
|
||||
-d) "$sz_program" e -si -so -t${sz_type} ;;
|
||||
*) "$sz_program" a f -si -so -t${sz_type} -mx${SZA_COMPRESSION_LEVEL:-9} ;;
|
||||
esac 2> /dev/null
|
@@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Vladimir Krivosheev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
@@ -1 +0,0 @@
|
||||
7-Zip precompiled binaries.
|
@@ -1,2 +0,0 @@
|
||||
export const path7za: string
|
||||
export const path7x: string
|
@@ -1,22 +0,0 @@
|
||||
"use strict"
|
||||
|
||||
const path = require("path")
|
||||
|
||||
function getPath() {
|
||||
if (process.env.USE_SYSTEM_7ZA === "true") {
|
||||
return "7za"
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
return path.join(__dirname, "mac", process.arch, "7za")
|
||||
}
|
||||
else if (process.platform === "win32") {
|
||||
return path.join(__dirname, "win", process.arch, "7za.exe")
|
||||
}
|
||||
else {
|
||||
return path.join(__dirname, "linux", process.arch, "7za")
|
||||
}
|
||||
}
|
||||
|
||||
exports.path7za = getPath()
|
||||
exports.path7x = path.join(__dirname, "7x.sh")
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
rm -rf /tmp/7z-linux
|
||||
mkdir /tmp/7z-linux
|
||||
cp "$BASEDIR/do-build.sh" /tmp/7z-linux/do-build.sh
|
||||
docker run --rm -v /tmp/7z-linux:/project buildpack-deps:xenial /project/do-build.sh
|
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
apt-get update -qq
|
||||
apt-get upgrade -qq
|
||||
|
||||
echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main" > /etc/apt/sources.list.d/llvm.list
|
||||
curl -L http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update -qq
|
||||
apt-get install -qq bzip2 yasm clang-5.0 lldb-5.0 lld-5.0
|
||||
|
||||
ln -s /usr/bin/clang-5.0 /usr/bin/clang
|
||||
ln -s /usr/bin/clang++-5.0 /usr/bin/clang++
|
||||
|
||||
mkdir -p /tmp/7z
|
||||
cd /tmp/7z
|
||||
curl -L http://downloads.sourceforge.net/project/p7zip/p7zip/16.02/p7zip_16.02_src_all.tar.bz2 | tar -xj -C . --strip-components 1
|
||||
cp makefile.linux_clang_amd64_asm makefile.machine
|
||||
make -j4
|
||||
mv bin/7za /project/7za
|
Binary file not shown.
Binary file not shown.
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "7zip-bin",
|
||||
"description": "7-Zip precompiled binaries",
|
||||
"version": "5.1.1",
|
||||
"files": [
|
||||
"*.js",
|
||||
"7x.sh",
|
||||
"index.d.ts",
|
||||
"linux",
|
||||
"mac",
|
||||
"win"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": "develar/7zip-bin",
|
||||
"keywords": [
|
||||
"7zip",
|
||||
"7z",
|
||||
"7za"
|
||||
]
|
||||
}
|
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"id": "io.github.jackgruber.backup",
|
||||
"app_min_version": "2.1.3",
|
||||
"version": "1.1.1",
|
||||
"name": "Simple Backup",
|
||||
"description": "Plugin to create manual and automatic backups.",
|
||||
"author": "JackGruber",
|
||||
"homepage_url": "https://github.com/JackGruber/joplin-plugin-backup/blob/master/README.md",
|
||||
"repository_url": "https://github.com/JackGruber/joplin-plugin-backup",
|
||||
"keywords": ["backup", "jex", "export", "zip", "7zip", "encrypted"]
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
#joplin-plugin-content {
|
||||
width: fit-content;
|
||||
background-color: var(--joplin-background-color);
|
||||
color: var(--joplin-color);
|
||||
}
|
||||
|
||||
#backuperror {
|
||||
width: fit-content;
|
||||
display: block;
|
||||
flex-direction: column;
|
||||
min-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
font-size: var(--joplin-font-size);
|
||||
font-family: var(--joplin-font-family);
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"id": "plugin.calebjohn.rich-markdown",
|
||||
"app_min_version": "2.4",
|
||||
"version": "0.8.3",
|
||||
"name": "Rich Markdown",
|
||||
"description": "Helping you ditch the markdown viewer for good.",
|
||||
"author": "Caleb John",
|
||||
"homepage_url": "https://github.com/CalebJohn/joplin-rich-markdown#readme",
|
||||
"repository_url": "https://github.com/CalebJohn/joplin-rich-markdown",
|
||||
"keywords": [
|
||||
"editor",
|
||||
"visual"
|
||||
],
|
||||
"_publish_hash": "sha256:7059ff05f3fcac2ead5b8f4cb04ec66830be55e02407408a39ab20e536055f5d",
|
||||
"_publish_commit": "main:9816b21068ce09014e8ef8adb0bd31bc20343247"
|
||||
}
|
Binary file not shown.
@@ -6,7 +6,6 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dist": "yarn run electronRebuild && npx electron-builder",
|
||||
"bundleDefaultPlugins": "cd ../tools && node bundleDefaultPlugins.js",
|
||||
"build": "gulp build",
|
||||
"postinstall": "yarn run build",
|
||||
"electronBuilder": "gulp electronBuilder",
|
||||
@@ -91,11 +90,7 @@
|
||||
"CFBundleURLName": "org.joplinapp.x-callback-url"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binaries": [
|
||||
"Contents/Resources/build/defaultPlugins/io.github.jackgruber.backup/7zip-bin/mac/arm64/7za",
|
||||
"Contents/Resources/build/defaultPlugins/io.github.jackgruber.backup/7zip-bin/mac/x64/7za"
|
||||
]
|
||||
}
|
||||
},
|
||||
"linux": {
|
||||
"icon": "../../Assets/LinuxIcons",
|
||||
@@ -112,8 +107,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/laurent22/joplin#readme",
|
||||
"devDependencies": {
|
||||
"@electron/notarize": "1.2.3",
|
||||
"@electron/rebuild": "3.2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/jest": "29.2.6",
|
||||
@@ -122,7 +115,9 @@
|
||||
"@types/react-redux": "7.1.25",
|
||||
"@types/styled-components": "5.1.26",
|
||||
"electron": "19.1.4",
|
||||
"electron-builder": "24.1.2",
|
||||
"electron-builder": "23.6.0",
|
||||
"electron-notarize": "1.2.2",
|
||||
"electron-rebuild": "3.2.9",
|
||||
"glob": "8.1.0",
|
||||
"gulp": "4.0.2",
|
||||
"jest": "29.4.3",
|
||||
@@ -181,4 +176,4 @@
|
||||
"taboverride": "4.0.3",
|
||||
"tinymce": "5.10.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const electron_notarize = require('@electron/notarize');
|
||||
const electron_notarize = require('electron-notarize');
|
||||
const execCommand = require('./execCommand');
|
||||
|
||||
function isDesktopAppTag(tagName) {
|
||||
|
@@ -150,8 +150,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097687
|
||||
versionName "2.11.2"
|
||||
versionCode 2097685
|
||||
versionName "2.11.0"
|
||||
// ndk {
|
||||
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
// }
|
||||
|
@@ -3,57 +3,62 @@
|
||||
*/
|
||||
|
||||
|
||||
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';
|
||||
|
||||
import { expect, describe, it } from '@jest/globals';
|
||||
// Randomly fails on:
|
||||
//
|
||||
// > 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', () => {
|
||||
// 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');
|
||||
it('should succeed', async () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 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,12 +2,9 @@ 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;
|
||||
@@ -21,31 +18,23 @@ 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.enabled || !initialPromptDone);
|
||||
const [display, setDisplay] = useState(!!props.sensorInfo.supportedSensors && (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 biometricAuthenticate();
|
||||
await FingerprintScanner.authenticate({ description: _('Verify your identity') });
|
||||
setTryBiometricsCheck(false);
|
||||
setDisplay(false);
|
||||
} catch (error) {
|
||||
Alert.alert(error.message);
|
||||
Alert.alert(_('Could not verify your identify'), error.message);
|
||||
setTryBiometricsCheck(false);
|
||||
} finally {
|
||||
FingerprintScanner.release();
|
||||
}
|
||||
|
||||
setTryBiometricsCheck(false);
|
||||
|
||||
logger.info('biometricsCheck: end');
|
||||
};
|
||||
|
||||
void biometricsCheck();
|
||||
@@ -56,9 +45,6 @@ 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);
|
||||
@@ -73,8 +59,6 @@ export default (props: Props) => {
|
||||
type: 'BIOMETRICS_DONE_SET',
|
||||
value: true,
|
||||
});
|
||||
|
||||
logger.info('complete: end');
|
||||
};
|
||||
|
||||
Alert.alert(
|
||||
@@ -93,7 +77,7 @@ export default (props: Props) => {
|
||||
},
|
||||
]
|
||||
);
|
||||
}, [initialPromptDone, display, props.dispatch]);
|
||||
}, [initialPromptDone, props.sensorInfo.supportedSensors, display, props.dispatch]);
|
||||
|
||||
const windowSize = useMemo(() => {
|
||||
return {
|
||||
@@ -103,18 +87,12 @@ 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 = () => {
|
||||
|
@@ -1,28 +0,0 @@
|
||||
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();
|
||||
}
|
||||
};
|
@@ -1,7 +1,5 @@
|
||||
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;
|
||||
@@ -14,9 +12,6 @@ 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,
|
||||
@@ -29,21 +24,7 @@ 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) {
|
||||
@@ -53,7 +34,7 @@ export default async (): Promise<SensorInfo> => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Could not check for biometrics sensor:', error);
|
||||
console.warn('Could not check for biometrics sensor:', error);
|
||||
Setting.setValue('security.biometricsSupportedSensors', '');
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,6 @@ 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 {
|
||||
@@ -464,7 +463,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) => void updateSettingValue(key, value)} />
|
||||
<Switch key="control" style={this.styles().switchSettingControl} trackColor={{ false: theme.dividerColor }} value={value} onValueChange={(value: any) => updateSettingValue(key, value)} />
|
||||
</View>
|
||||
{descriptionComp}
|
||||
</View>
|
||||
@@ -475,39 +474,13 @@ 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 = async (key: string, value: any) => {
|
||||
const handled = await this.handleSetting(key, value);
|
||||
if (!handled) shared.updateSettingValue(this, key, value);
|
||||
const updateSettingValue = (key: string, value: any) => {
|
||||
return shared.updateSettingValue(this, key, value);
|
||||
};
|
||||
|
||||
const md = Setting.settingMetadata(key);
|
||||
@@ -544,7 +517,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
fontSize: theme.fontSize,
|
||||
}}
|
||||
onValueChange={(itemValue: string) => {
|
||||
void updateSettingValue(key, itemValue);
|
||||
updateSettingValue(key, itemValue);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -580,7 +553,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 => void updateSettingValue(key, value)} />
|
||||
<Slider key="control" style={{ flex: 1 }} step={md.step} minimumValue={minimum} maximumValue={maximum} value={value} onValueChange={value => updateSettingValue(key, value)} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -604,7 +577,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) => void 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) => updateSettingValue(key, value)} secureTextEntry={!!md.secure} />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
|
@@ -328,7 +328,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-image-resizer (1.4.5):
|
||||
- React-Core
|
||||
- react-native-netinfo (9.3.8):
|
||||
- react-native-netinfo (9.3.7):
|
||||
- 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: fbc23bc2fe217155d85f2f7e0644b1654df8029b
|
||||
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
|
||||
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
||||
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
|
||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||
|
@@ -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.8",
|
||||
"@react-native-community/netinfo": "9.3.7",
|
||||
"@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.5.0",
|
||||
"react-native-paper": "5.4.1",
|
||||
"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.2.2",
|
||||
"@codemirror/commands": "6.1.2",
|
||||
"@codemirror/lang-cpp": "6.0.2",
|
||||
"@codemirror/lang-html": "6.4.3",
|
||||
"@codemirror/lang-html": "6.4.0",
|
||||
"@codemirror/lang-java": "6.0.1",
|
||||
"@codemirror/lang-javascript": "6.1.5",
|
||||
"@codemirror/lang-markdown": "6.1.0",
|
||||
"@codemirror/lang-javascript": "6.1.1",
|
||||
"@codemirror/lang-markdown": "6.0.5",
|
||||
"@codemirror/lang-php": "6.0.1",
|
||||
"@codemirror/lang-rust": "6.0.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",
|
||||
"@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",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@lezer/highlight": "1.1.4",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
|
@@ -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, '-3') });
|
||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-1') });
|
||||
|
||||
// await db.clearForTesting();
|
||||
}
|
||||
@@ -984,10 +984,6 @@ 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
|
||||
|
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "2.4.2",
|
||||
"slugify": "1.6.6",
|
||||
"slugify": "1.6.5",
|
||||
"yeoman-generator": "5.8.0",
|
||||
"yosay": "2.0.2"
|
||||
},
|
||||
|
@@ -41,7 +41,7 @@ async function downloadFile(url: string, outputPath: string) {
|
||||
|
||||
export async function extractPlugins(currentDir: string, defaultPluginDir: string, downloadedPluginsNames: PluginIdAndName): Promise<void> {
|
||||
for (const pluginId of Object.keys(downloadedPluginsNames)) {
|
||||
await execCommand(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: true, splitCommandOptions: { handleEscape: false } });
|
||||
await execCommand(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: 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 remove(`${downloadedPluginsNames[pluginId]}`);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as execa from 'execa';
|
||||
import commandToString from './commandToString';
|
||||
import splitCommandString, { SplitCommandOptions } from './splitCommandString';
|
||||
import splitCommandString from './splitCommandString';
|
||||
import { stdout } from 'process';
|
||||
|
||||
interface ExecCommandOptions {
|
||||
@@ -8,7 +8,6 @@ interface ExecCommandOptions {
|
||||
showStdout?: boolean;
|
||||
showStderr?: boolean;
|
||||
quiet?: boolean;
|
||||
splitCommandOptions?: SplitCommandOptions | null;
|
||||
}
|
||||
|
||||
export default async (command: string | string[], options: ExecCommandOptions | null = null): Promise<string> => {
|
||||
@@ -34,7 +33,7 @@ export default async (command: string | string[], options: ExecCommandOptions |
|
||||
}
|
||||
}
|
||||
|
||||
const args: string[] = typeof command === 'string' ? splitCommandString(command, options.splitCommandOptions || null) : command as string[];
|
||||
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
|
||||
const executableName = args[0];
|
||||
args.splice(0, 1);
|
||||
const promise = execa(executableName, args);
|
||||
|
@@ -1,8 +1,4 @@
|
||||
export interface SplitCommandOptions {
|
||||
handleEscape?: boolean;
|
||||
}
|
||||
|
||||
export default (command: string, options: SplitCommandOptions | null = null) => {
|
||||
export default (command: string, options: any = null) => {
|
||||
options = options || {};
|
||||
if (!('handleEscape' in options)) {
|
||||
options.handleEscape = true;
|
||||
|
@@ -1,20 +1,5 @@
|
||||
# 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)
|
||||
|
@@ -423,22 +423,6 @@
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
# 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.
|
@@ -1,39 +0,0 @@
|
||||
# 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)
|
@@ -4,13 +4,13 @@
|
||||
"config:base"
|
||||
],
|
||||
"major": {
|
||||
"stabilityDays": 80,
|
||||
"stabilityDays": 30,
|
||||
},
|
||||
"minor": {
|
||||
"stabilityDays": 40,
|
||||
"stabilityDays": 20,
|
||||
},
|
||||
"patch": {
|
||||
"stabilityDays": 20,
|
||||
"stabilityDays": 10,
|
||||
},
|
||||
"prConcurrentLimit": 5,
|
||||
"prHourlyLimit": 0,
|
||||
@@ -38,7 +38,6 @@
|
||||
"@codemirror/search",
|
||||
"@codemirror/state",
|
||||
"@codemirror/view",
|
||||
"@lezer/highlight",
|
||||
"@fortawesome/fontawesome-svg-core",
|
||||
"@fortawesome/free-solid-svg-icons",
|
||||
"@svgr/webpack",
|
||||
|
Reference in New Issue
Block a user