Mobile: Upgraded React Native to v0.63

commit 2fb6cee901
Merge: 4e303be85f db509955f6
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 16:24:07 2020 +0100

    Merge branch 'dev' into rn_63

commit 4e303be85f
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 16:14:39 2020 +0100

    Clean up

commit e3a37ec2d6
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 15:57:55 2020 +0100

    Use different script for pre-commit and manual start

commit bd236648fc
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 15:56:45 2020 +0100

    Removed RN eslint config

commit e7feda41c9
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 15:27:08 2020 +0100

    Revert "Disable git hook for now"

    This reverts commit 89263ac742.

commit cfd63fe46f
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 13:02:32 2020 +0100

    Ask permission to use geo-location

commit 66059939a3
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 12:26:20 2020 +0100

    Fixed WebView race condition

commit 1e0d2b7b86
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 11:56:21 2020 +0100

    Fixed webview issues

commit f537d22d7f
Author: Laurent Cozic <laurent@cozic.net>
Date:   Fri Oct 16 11:08:29 2020 +0100

    Improve resource file watching

commit eec32cf70a
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 18:40:13 2020 +0100

    Removed cache package dependency and implemented one more suitable for React Native

commit efa346fea4
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 14:57:21 2020 +0100

    iOS: Added fonts to Info.plist although it was working without it

commit 572b647bc0
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 14:56:49 2020 +0100

    Specify content-type header for OneDrive to prevent network error

commit bcedf6c7f0
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 12:45:01 2020 +0100

    iOS: Disable long press menu since it is already built-in

commit 7359dd61d1
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 12:37:40 2020 +0100

    Removed unused react-native-device-info

commit 2d63ab36d3
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 12:35:54 2020 +0100

    iOS: Fixed taking a picture

commit 8e2875a91c
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 12:11:13 2020 +0100

    iOS: Restored camera roll functionality

commit 75f5edf2ad
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 11:40:13 2020 +0100

    iOS: Fixed build settings

commit b220c98419
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 11:40:03 2020 +0100

    iOS: Got images to work with WebKit

commit c34b43e841
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 10:24:52 2020 +0100

    iOS: Restore more settings

commit 32997611e6
Author: Laurent Cozic <laurent@cozic.net>
Date:   Thu Oct 15 10:15:14 2020 +0100

    iOS: Added back icons and other properties

commit b5811d7f7c
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 23:53:14 2020 +0100

    Got iOS build to work

commit dc6d7c00e0
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 18:40:06 2020 +0100

    Imported old settings in gradle build

commit dff59f5603
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 18:20:00 2020 +0100

    Restored sharing

commit 0bdb449e72
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 17:25:40 2020 +0100

    Updated NoteBodyViewer

commit 0c0d228815
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 16:54:42 2020 +0100

    Fixed networking

commit 6ff45ce485
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 13:11:00 2020 +0100

    Fixed document picker

commit cc889182b6
Author: Laurent Cozic <laurent@cozic.net>
Date:   Wed Oct 14 12:56:27 2020 +0100

    Added back support for alarms

commit 040261abfa
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 22:04:49 2020 +0100

    Fixed Clipboard and remove image-picker package

commit 1077ad8f16
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 21:54:52 2020 +0100

    Fixed Select Alarm dialog and PoorManIntervals class

commit 8296676fd5
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 21:32:52 2020 +0100

    Fixed icons and warnings

commit 3b0e3f6f43
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 17:02:59 2020 +0100

    Got app to build again

commit 89263ac742
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 15:41:17 2020 +0100

    Disable git hook for now

commit d6da162f67
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 15:39:12 2020 +0100

    Restored back all RN packages

commit 7f8ce3732c
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 15:13:12 2020 +0100

    Restored base packages

commit ea59726eb3
Author: Laurent Cozic <laurent@cozic.net>
Date:   Tue Oct 13 15:05:17 2020 +0100

    Started over from scratch
pull/3936/head
Laurent Cozic 2020-10-16 16:26:19 +01:00
parent db509955f6
commit cb3e1cf1e9
132 changed files with 6168 additions and 6461 deletions

View File

@ -64,6 +64,7 @@ CliClient/build/
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
CliClient/app/services/plugins/PluginRunner.js
CliClient/tests/InMemoryCache.js
CliClient/tests/models_Setting.js
CliClient/tests/services_CommandService.js
CliClient/tests/services_InteropService.js
@ -201,13 +202,21 @@ ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/BackButtonDialogBox.js
ReactNativeClient/lib/components/CameraView.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnMessage.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useSource.js
ReactNativeClient/lib/components/NoteBodyViewer/NoteBodyViewer.js
ReactNativeClient/lib/components/screens/Note.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/components/SelectDateTimeDialog.js
ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/hooks/usePropsDebugger.js
ReactNativeClient/lib/InMemoryCache.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
@ -219,6 +228,7 @@ ReactNativeClient/lib/markdownUtils.js
ReactNativeClient/lib/models/Alarm.js
ReactNativeClient/lib/models/Setting.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/PoorManIntervals.js
ReactNativeClient/lib/reducer.js
ReactNativeClient/lib/services/AlarmService.js
ReactNativeClient/lib/services/AlarmServiceDriver.android.js

10
.gitignore vendored
View File

@ -58,6 +58,7 @@ plugin_types/
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
CliClient/app/services/plugins/PluginRunner.js
CliClient/tests/InMemoryCache.js
CliClient/tests/models_Setting.js
CliClient/tests/services_CommandService.js
CliClient/tests/services_InteropService.js
@ -195,13 +196,21 @@ ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/BackButtonDialogBox.js
ReactNativeClient/lib/components/CameraView.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnMessage.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useSource.js
ReactNativeClient/lib/components/NoteBodyViewer/NoteBodyViewer.js
ReactNativeClient/lib/components/screens/Note.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/components/SelectDateTimeDialog.js
ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/hooks/usePropsDebugger.js
ReactNativeClient/lib/InMemoryCache.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
@ -213,6 +222,7 @@ ReactNativeClient/lib/markdownUtils.js
ReactNativeClient/lib/models/Alarm.js
ReactNativeClient/lib/models/Setting.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/PoorManIntervals.js
ReactNativeClient/lib/reducer.js
ReactNativeClient/lib/services/AlarmService.js
ReactNativeClient/lib/services/AlarmServiceDriver.android.js

View File

@ -4199,11 +4199,6 @@
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
},
"memory-cache": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo="
},
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",

View File

@ -6,7 +6,7 @@
"scripts": {
"test": "gulp buildTests -L && node node_modules/jasmine/bin/jasmine.js --fail-fast=true --config=tests/support/jasmine.json",
"test-ci": "gulp buildTests -L && node node_modules/jasmine/bin/jasmine.js --config=tests/support/jasmine.json",
"postinstall": "npm run build && patch-package --patch-dir ../patches",
"postinstall": "npm run build && patch-package --patch-dir ../patches/shared && patch-package --patch-dir ../patches/node",
"build": "gulp build",
"start": "gulp build -L && node 'build/main.js' --stack-trace-enabled --log-level debug --env dev"
},
@ -80,7 +80,6 @@
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"md5-file": "^4.0.0",
"memory-cache": "^0.2.0",
"mime": "^2.0.3",
"moment": "^2.24.0",
"multiparty": "^4.2.1",

View File

@ -0,0 +1,59 @@
import InMemoryCache from 'lib/InMemoryCache';
const { time } = require('lib/time-utils.js');
describe('InMemoryCache', function() {
it('should get and set values', () => {
const cache = new InMemoryCache();
expect(cache.value('test')).toBe(undefined);
expect(cache.value('test', 'default')).toBe('default');
cache.setValue('test', 'something');
expect(cache.value('test')).toBe('something');
// Check we get the exact same object back (cache should not copy)
const someObj = { abcd: '123' };
cache.setValue('someObj', someObj);
expect(cache.value('someObj')).toBe(someObj);
});
it('should expire values', async () => {
const cache = new InMemoryCache();
// Check that the value is udefined once the cache has expired
cache.setValue('test', 'something', 500);
expect(cache.value('test')).toBe('something');
await time.msleep(510);
expect(cache.value('test')).toBe(undefined);
// Check that the TTL is reset every time setValue is called
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
expect(cache.value('test')).toBe('something');
});
it('should delete old records', async () => {
const cache = new InMemoryCache(5);
cache.setValue('1', '1');
cache.setValue('2', '2');
cache.setValue('3', '3');
cache.setValue('4', '4');
cache.setValue('5', '5');
expect(cache.value('1')).toBe('1');
cache.setValue('6', '6');
expect(cache.value('1')).toBe(undefined);
});
});

View File

@ -146,7 +146,7 @@ describe('services_rest_Api', function() {
}));
const noteId = response.id;
{
const note = await Note.load(noteId);
expect(note.latitude).toBe('48.73207100');
@ -154,7 +154,7 @@ describe('services_rest_Api', function() {
expect(note.altitude).toBe('21.0000');
}
await api.route('PUT', 'notes/' + noteId, null, JSON.stringify({
await api.route('PUT', `notes/${noteId}`, null, JSON.stringify({
latitude: '49',
longitude: '-3',
altitude: '22',

View File

@ -8061,11 +8061,6 @@
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz",
"integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA=="
},
"memory-cache": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo="
},
"mermaid": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.8.1.tgz",

View File

@ -5,7 +5,7 @@
"main": "main.js",
"scripts": {
"dist": "node_modules/.bin/electron-builder",
"build": "patch-package --patch-dir ../patches && gulp build",
"build": "patch-package --patch-dir ../patches/shared && patch-package --patch-dir ../patches/node && gulp build",
"postinstall": "npm run build && gulp electronRebuild",
"start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools"
},
@ -169,7 +169,6 @@
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"md5-file": "^4.0.0",
"memory-cache": "^0.2.0",
"mermaid": "^8.8.1",
"moment": "^2.22.2",
"multiparty": "^4.2.1",

View File

@ -21,7 +21,7 @@ node_modules/warning/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/interface.js
node_modules/react-native/flow/
[options]
@ -36,9 +36,8 @@ module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
@ -57,7 +56,6 @@ untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
@ -72,4 +70,4 @@ untyped-import
untyped-type-import
[version]
^0.105.0
^0.122.0

View File

@ -20,7 +20,6 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
@ -42,29 +41,19 @@ buck-out/
*.keystore
!debug.keystore
# CocoaPods
ios/Pods/
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
# https://docs.fastlane.tools/best-practices/source-control/
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# This is generated:
android/build*
android/app/build*
# Bundle artifact
*.jsbundle
android/.project
android/.settings/
android/app/.classpath
android/app/.project
android/app/.settings/
android/app/src/debug/res/
pluginAssets/
# CocoaPods
/ios/Pods/

View File

@ -0,0 +1,6 @@
module.exports = {
bracketSpacing: false,
jsxBracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
};

View File

@ -13,7 +13,7 @@ import {
Image,
} from 'react-native';
import { renderFormatButtons } from './renderButtons';
import { NoteBodyViewer } from 'lib/components/note-body-viewer.js';
import NoteBodyViewer from 'lib/components/NoteBodyViewer/NoteBodyViewer';
const styles = StyleSheet.create({
buttonContainer: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -15,9 +15,14 @@ import com.android.build.OutputFile
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
@ -33,6 +38,13 @@ import com.android.build.OutputFile
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
@ -66,7 +78,6 @@ import com.android.build.OutputFile
*/
project.ext.react = [
entryFile: "index.android.js",
enableHermes: false, // clean and rebuild if changing
]
@ -99,6 +110,7 @@ def enableProguardInReleaseBuilds = false
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
@ -111,18 +123,13 @@ def enableHermes = project.ext.react.get("enableHermes", false);
android {
compileSdkVersion rootProject.ext.compileSdkVersion
// To fix this error:
// > java.io.UncheckedIOException: java.io.IOException: Execution of compression failed. java.lang.OutOfMemoryError
// https://stackoverflow.com/q/57284812/561309
dexOptions { javaMaxHeapSize "4g" }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "net.cozic.joplin"
defaultConfig {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097583
@ -130,18 +137,23 @@ android {
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
missingDimensionStrategy 'react-native-camera', 'general'
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
// https://github.com/react-native-community/react-native-camera/issues/2138
missingDimensionStrategy 'react-native-camera', 'general'
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
multiDexEnabled true
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
@ -156,57 +168,54 @@ android {
}
}
}
buildTypes {
debug {
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation "org.webkit:android-jsc:r241213"
compile project(':react-native-push-notification')
// implementation (project(':react-native-camera')) {
// // This is required because com.google.firebase requires v16.0.x of com.google.android.gms
// // while react-native-camera requires v15.x, which results in broken dependencies with
// // this error message:
// //
// // The library com.google.android.gms:play-services-base is being requested by various other libraries at [[15.0.1,15.0.1]], but resolves to 16.0.1
// //
// // For the record: found solution by removing all Firebase stuff here and running "gradlew.bat :app:dependencies"
// // That shows that react-native-camera was the one requiring v15.0.1.
// exclude group: "com.google.android.gms"
// }
implementation project(':react-native-file-viewer')
implementation project(':react-native-securerandom')
implementation project(':react-native-fs')
implementation project(':react-native-image-picker')
implementation project(':react-native-vector-icons')
implementation project(':react-native-fs')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:28.0.0"
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
@ -216,44 +225,16 @@ dependencies {
implementation jscFlavor
}
implementation project(':react-native-sqlite-storage')
implementation project(':rn-fetch-blob')
implementation project(':react-native-document-picker')
implementation project(':react-native-image-resizer')
// implementation project(':react-native-share-extension')
implementation project(':react-native-version-info')
implementation project(':react-native-camera')
implementation "com.facebook.react:react-native:+"
implementation project(':react-native-quick-actions')
// implementation "com.google.android.gms:play-services-base:16.0.1" // For Firebase
// implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // For Firebase - this line if you wish to use badge on Android
// To fix the error below, which happened after adding react-native-camera.
// Doesn't make any sense since rn-camera neither defines v26 nor 27 but
// v25.0.2 in build.gradle, but anyway now it works ¯\_()_/¯
// --------------------------------------------------------------------------------------
// Fatal error
// { Error: Command failed: ./gradlew assembleRelease -PbuildDir=build --console plain
//
// FAILURE: Build failed with an exception.
//
// * What went wrong:
// Execution failed for task ':app:preReleaseBuild'.
// > Android dependency 'com.android.support:support-v4' has different version for the compile (26.1.0) and runtime (27.1.1) classpath. You should manually set the same version via DependencyResolution
// --------------------------------------------------------------------------------------
// https://github.com/react-native-community/react-native-camera/issues/1532#issuecomment-386434771
compile ("com.android.support:support-v4:26.0.1") {
force = true //<-- force dependency resolution to 26.0.1 in my case
}
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
implementation 'com.android.support:multidex:2.0.1'
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
from configurations.compile
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

View File

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

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
</manifest>
</manifest>

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package net.cozic.joplin;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}

View File

@ -1,111 +1,89 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto"
package="net.cozic.joplin"
android:versionCode="2"
android:versionName="0.8.0">
package="net.cozic.joplin">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<!-- ============================= -->
<!-- START RN-push-notitication -->
<!-- ============================= -->
<!-- RN-NOTIFICATION -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- ============================= -->
<!-- END RN-push-notitication -->
<!-- ============================= -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- /RN-NOTIFICATION -->
<!-- Make these features optional to enable Chromebooks -->
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="26" />
<application
android:name=".MainApplication"
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true"
android:largeHeap="true"
>
<!-- ============================= -->
<!-- START RN-push-notitication -->
<!-- ============================= -->
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_name" android:value="@string/default_notification_channel_id"/>
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_description" android:value="Allow displaying a notification for Joplin's alarms"/>
<meta-data android:name="com.dieam.reactnativepushnotification.notification_color" android:resource="@android:color/white"/>
android:theme="@style/AppTheme">
<!-- RN-NOTIFICATION -->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="${applicationId}" />
</intent-filter>
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_DISMISS" />
<action android:name="ACTION_SNOOZE" />
</intent-filter>
</receiver>
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
android:directBootAware="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- /RN-NOTIFICATION -->
<service
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerServiceGcm"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<!-- ============================= -->
<!-- END RN-push-notitication -->
<!-- ============================= -->
<!--
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.
2020-10-06: Changed back again to "singleInstance" and notifications still seem to work. Changing to singleInstance
to try to fix this bug: https://discourse.joplinapp.org/t/joplin-android-app-looses-nextcloud-sync-settings/10997/6
Users would lose their settings, and it's possibly due to multiple instances of the app running at the same time, perhaps
due to sharing with the app. When checking the log, it would show "saving settings", then the app startup message, and after the app
has started, it would show "setting saved". So basically the app, or one instance of it, has started while settings were being saved
2020-10-08: Changed back again to "singleTop" as it has worked so far. The multiple instance bug was "fixed" in a different way
See ReactNativeClient/root.js for more info about the bug.
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.
2020-10-06: Changed back again to "singleInstance" and notifications still seem to work. Changing to singleInstance
to try to fix this bug: https://discourse.joplinapp.org/t/joplin-android-app-looses-nextcloud-sync-settings/10997/6
Users would lose their settings, and it's possibly due to multiple instances of the app running at the same time, perhaps
due to sharing with the app. When checking the log, it would show "saving settings", then the app startup message, and after the app
has started, it would show "setting saved". So basically the app, or one instance of it, has started while settings were being saved
2020-10-08: Changed back again to "singleTop" as it has worked so far. The multiple instance bug was "fixed" in a different way
See ReactNativeClient/root.js for more info about the bug.
-->
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTop">
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<!-- SHARE EXTENSION -->
<activity
android:noHistory="true"
android:name=".share.ShareActivity"
@ -124,6 +102,8 @@
<data android:mimeType="*/*" />
</intent-filter>
</activity>
</application>
<!-- /SHARE EXTENSION -->
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -12,4 +12,4 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "Joplin";
}
}
}

View File

@ -3,24 +3,28 @@ package net.cozic.joplin;
import android.app.Application;
import android.content.Context;
import android.database.CursorWindow;
import android.os.Build;
import android.webkit.WebView;
import androidx.multidex.MultiDex;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import net.cozic.joplin.share.SharePackage;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import net.cozic.joplin.share.SharePackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
// Needed to fix: The number of method references in a .dex file cannot exceed 64K
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
@ -29,6 +33,7 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new SharePackage());
@ -41,23 +46,16 @@ public class MainApplication extends Application implements ReactApplication {
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
@Override
public void onCreate() {
super.onCreate();
// Enable debugging with the WebView we use to display notes
// Changes are made as recommended by folks at `react-native-webview`
// https://github.com/react-native-community/react-native-webview/blob/master/docs/Debugging.md
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
// To try to fix the error "Row too big to fit into CursorWindow"
// To try to fix the error "Row too big to fit into CursorWindow"
// https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153
// https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991
try {
@ -66,25 +64,31 @@ public class MainApplication extends Application implements ReactApplication {
field.set(null, 50 * 1024 * 1024); //the 102400 is the new size added
} catch (Exception e) {
e.printStackTrace();
}
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this); // Remove this line if you don't want Flipper enabled
}
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates.
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(Context context) {
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
Class<?> aClass = Class.forName("net.cozic.joplin.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
@ -96,4 +100,4 @@ public class MainApplication extends Application implements ReactApplication {
}
}
}
}
}

View File

@ -1,3 +0,0 @@
<resources>
<color name="white">#FFF</color>
</resources>

View File

@ -2,18 +2,17 @@
buildscript {
ext {
buildToolsVersion = "28.0.3"
buildToolsVersion = "29.0.2"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
compileSdkVersion = 29
targetSdkVersion = 29
}
repositories {
jcenter()
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.4.2")
classpath("com.android.tools.build:gradle:3.5.3")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@ -22,10 +21,6 @@ buildscript {
allprojects {
repositories {
mavenLocal()
maven { url "https://jitpack.io" }
maven {
url "https://maven.google.com"
}
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
@ -34,22 +29,9 @@ allprojects {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
jcenter()
maven { url 'https://jitpack.io' }
maven { url 'https://www.jitpack.io' }
}
}
// TODO: Maybe remove this (forgot why it was needed - probably to make some module work)
subprojects {
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
}
}
}
}

View File

@ -17,12 +17,12 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Required for react-native-webview
# https://github.com/react-native-community/react-native-webview/blob/master/docs/Getting-Started.md
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# To fix this error:
# > java.io.UncheckedIOException: java.io.IOException: Execution of compression failed. java.lang.OutOfMemoryError
# https://stackoverflow.com/q/57284812/561309
org.gradle.jvmargs=-Xmx4608m
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.54.0

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -7,7 +7,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@ -125,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@ -154,19 +154,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@ -5,14 +5,14 @@
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

View File

@ -1,17 +1,3 @@
rootProject.name = 'Joplin'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':app'
//include ':app', ':react-native-share-extension'
//project(':react-native-share-extension').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share-extension/android')
include ':react-native-push-notification'
project(':react-native-push-notification').projectDir = file('../node_modules/react-native-push-notification/android')
include ':react-native-camera'
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')

View File

@ -1,29 +1,4 @@
{
"name": "Joplin",
"displayName": "Joplin",
"expo": {
"name": "Joplin",
"description": "Joplin for Android",
"slug": "Joplin",
"privacy": "public",
"sdkVersion": "18.0.0",
"version": "1.0.0",
"orientation": "portrait",
"primaryColor": "#cccccc",
"icon": "./assets/icons/app-icon.png",
"notification": {
"icon": "./assets/icons/notification-icon.png",
"color": "#000000"
},
"loading": {
"icon": "./assets/icons/loading-icon.png",
"hideExponentText": false
},
"packagerOpts": {
"assetExts": ["ttf"]
},
"ios": {
"supportsTablet": true
}
}
"displayName": "Joplin"
}

View File

@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};

View File

@ -1,22 +0,0 @@
const gulp = require('gulp');
const utils = require('../Tools/gulp/utils');
const tasks = {
encodeAssets: {
fn: require('./tools/encodeAssets'),
},
buildReactNativeInjectedJs: {
fn: require('./tools/buildReactNativeInjectedJs'),
},
podInstall: {
fn: require('./tools/podInstall'),
},
};
utils.registerGulpTasks(gulp, tasks);
gulp.task('build', gulp.series(
'buildReactNativeInjectedJs',
'encodeAssets',
'podInstall'
));

View File

@ -1,3 +0,0 @@
const { main } = require('./main.js');
main();

View File

@ -1,3 +0,0 @@
const { main } = require('./main.js');
main();

View File

@ -1,3 +1,44 @@
const { main } = require('./main.js');
// Note about the application structure:
// - The user interface and its state is managed by React/Redux.
// - Persistent storage to SQLite and Web API is handled outside of React/Redux using regular JavaScript (no middleware, no thunk, etc.).
// - Communication from React to SQLite is done by calling model methods (note.save, etc.)
// - Communication from SQLite to Redux is done via dispatcher.
main();
// So there's basically still a one way flux: React => SQLite => Redux => React
import { LogBox, AppRegistry } from 'react-native';
const { Root } = require('./root.js');
// Seems JavaScript developers love adding warnings everywhere, even when these warnings can't be fixed
// or don't really matter. Because we want important warnings to actually be fixed, we disable
// all the useless ones, that way we aren't flooded by them when the app starts, and when there's
// one we know it should be fixed (or added here).
LogBox.ignoreLogs([
// Happens for example in react-native-side-menu, but the package is discontinued
// and we should just switch to a different one (or do it without a package).
'Animated: `useNativeDriver` was not specified. This is a required option and must be explicitly set to `true` or `false`',
// What's the point of printing warnings for non-user code. Of all the things that
// are broken and unreliable in React Native, require cycles are just a minor annoyance
// which shouldn't forever print warnings.
// To make it more fun, they don't normalise paths so forward slashes and backward slashes
// need to be handled to support Windows.
'Require cycle: node_modules/react-native-',
'Require cycle: node_modules\\react-native-',
'Require cycle: node_modules/rn-fetch-blob',
'Require cycle: node_modules\\rn-fetch-blob',
'Require cycle: node_modules/aws-sdk',
'Require cycle: node_modules\\aws-sdk',
// It's being updated over time and we don't need to see these warnings all the time
'Warning: componentWillReceiveProps has been renamed',
'Warning: componentWillUpdate has been renamed',
'Warning: componentWillMount has been renamed',
// Triggered by react-native-webview. Happens on slow devices when loading the note viewer.
// Apparently it can be safely ignored:
// https://github.com/react-native-webview/react-native-webview/issues/124
'Did not receive response to shouldStartLoad in time, defaulting to YES',
]);
AppRegistry.registerComponent('Joplin', () => Root);

View File

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -22,6 +22,19 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@ -36,19 +49,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSAppTransportSecurity</key>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -8,36 +8,37 @@
/* Begin PBXBuildFile section */
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
9E1B958F73B0E37D15AA9B24 /* libPods-Joplin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDE6F1BE6285C68A2DA17BE9 /* libPods-Joplin.a */; };
AE6BB3A2FDA34D01864A721A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 381C047F2739439CB3E6452A /* libRNVectorIcons.a */; };
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED297162215061F000B7C4FE /* JavaScriptCore.framework */; };
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; };
F3D0BB525E6C490294D73075 /* libRNSecureRandom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22647ACF9A4C45918C44C599 /* libRNSecureRandom.a */; };
FBF57CE2F0F448FA9A8985E2 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EB8BCAEA9AA41CAAE460443 /* libsqlite3.0.tbd */; };
46E31F54C547C341F605BB66 /* libPods-Joplin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
0EB8BCAEA9AA41CAAE460443 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* JoplinTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JoplinTests.m; sourceTree = "<group>"; };
09056573D4C040FBD5FEB93A /* Pods-Joplin-JoplinTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-JoplinTests.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-JoplinTests/Pods-Joplin-JoplinTests.debug.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Joplin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Joplin.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Joplin/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Joplin/AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Joplin/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Joplin/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Joplin/main.m; sourceTree = "<group>"; };
22647ACF9A4C45918C44C599 /* libRNSecureRandom.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSecureRandom.a; sourceTree = "<group>"; };
381C047F2739439CB3E6452A /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = "<group>"; };
4DA7F7A61FC1196F00353191 /* Joplin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Joplin.entitlements; path = Joplin/Joplin.entitlements; sourceTree = "<group>"; };
6CFFC13990302AAE61F81DB1 /* Pods-Joplin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.release.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.release.xcconfig"; sourceTree = "<group>"; };
756FF6C78FB548317DAD12C0 /* Pods-Joplin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.debug.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.debug.xcconfig"; sourceTree = "<group>"; };
DDE6F1BE6285C68A2DA17BE9 /* libPods-Joplin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
14868D674CC065C7BF1C9944 /* Pods-Joplin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.release.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.release.xcconfig"; sourceTree = "<group>"; };
245A6EBAE2E874DB706B16DB /* Pods-Joplin-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOS/Pods-Joplin-tvOS.release.xcconfig"; sourceTree = "<group>"; };
258F823D616BE3D6A52BC900 /* libPods-Joplin-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2C91CD1424C7137D07789148 /* Pods-Joplin-JoplinTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-JoplinTests.release.xcconfig"; path = "Target Support Files/Pods-Joplin-JoplinTests/Pods-Joplin-JoplinTests.release.xcconfig"; sourceTree = "<group>"; };
2DA44D9A347489A29B995F73 /* Pods-Joplin-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOSTests/Pods-Joplin-tvOSTests.debug.xcconfig"; sourceTree = "<group>"; };
37DBC181C4AD99CBE0D07EEB /* Pods-Joplin-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOSTests/Pods-Joplin-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
505CB61090817F4453631957 /* Pods-Joplin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin.debug.xcconfig"; path = "Target Support Files/Pods-Joplin/Pods-Joplin.debug.xcconfig"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Joplin/LaunchScreen.storyboard; sourceTree = "<group>"; };
A3FEB746EE7F1B0FF28528E1 /* Pods-Joplin-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Joplin-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Joplin-tvOS/Pods-Joplin-tvOS.debug.xcconfig"; sourceTree = "<group>"; };
A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
B61798F36B3BC123BF8EA4D9 /* libPods-Joplin-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
F5E37D05726A4A08B2EE323A /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
F69B873C692CE22F1C4C9264 /* libPods-Joplin-JoplinTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Joplin-JoplinTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -45,68 +46,55 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */,
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */,
FBF57CE2F0F448FA9A8985E2 /* libsqlite3.0.tbd in Frameworks */,
AE6BB3A2FDA34D01864A721A /* libRNVectorIcons.a in Frameworks */,
F3D0BB525E6C490294D73075 /* libRNSecureRandom.a in Frameworks */,
9E1B958F73B0E37D15AA9B24 /* libPods-Joplin.a in Frameworks */,
46E31F54C547C341F605BB66 /* libPods-Joplin.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
04FA5E9085024E3A9DAF0E03 /* Frameworks */ = {
00E356EF1AD99517003FC87E /* JoplinTests */ = {
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
0EB8BCAEA9AA41CAAE460443 /* libsqlite3.0.tbd */,
DDE6F1BE6285C68A2DA17BE9 /* libPods-Joplin.a */,
00E356F21AD99517003FC87E /* JoplinTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
name = Frameworks;
path = JoplinTests;
sourceTree = "<group>";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
isa = PBXGroup;
children = (
00E356F11AD99517003FC87E /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* Joplin */ = {
isa = PBXGroup;
children = (
4DA7F7A61FC1196F00353191 /* Joplin.entitlements */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = Joplin;
sourceTree = "<group>";
};
4D2A85911FBCE3950028537D /* Recovered References */ = {
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
F5E37D05726A4A08B2EE323A /* libRNFetchBlob.a */,
381C047F2739439CB3E6452A /* libRNVectorIcons.a */,
22647ACF9A4C45918C44C599 /* libRNSecureRandom.a */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
B61798F36B3BC123BF8EA4D9 /* libPods-Joplin-tvOS.a */,
258F823D616BE3D6A52BC900 /* libPods-Joplin-tvOSTests.a */,
A5E1CD825FABD6C4E704EA54 /* libPods-Joplin.a */,
F69B873C692CE22F1C4C9264 /* libPods-Joplin-JoplinTests.a */,
);
name = "Recovered References";
sourceTree = "<group>";
};
5F0B55ADB94AB5BB22C598AC /* Pods */ = {
isa = PBXGroup;
children = (
756FF6C78FB548317DAD12C0 /* Pods-Joplin.debug.xcconfig */,
6CFFC13990302AAE61F81DB1 /* Pods-Joplin.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
7FDFE2A137C24ED08645158D /* Resources */ = {
isa = PBXGroup;
children = (
);
name = Resources;
name = Frameworks;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
@ -121,11 +109,10 @@
children = (
13B07FAE1A68108700A75B9A /* Joplin */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* JoplinTests */,
83CBBA001A601CBA00E9B192 /* Products */,
04FA5E9085024E3A9DAF0E03 /* Frameworks */,
7FDFE2A137C24ED08645158D /* Resources */,
4D2A85911FBCE3950028537D /* Recovered References */,
5F0B55ADB94AB5BB22C598AC /* Pods */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
9CDB1D9DB6483D893504BFCB /* Pods */,
);
indentWidth = 2;
sourceTree = "<group>";
@ -140,6 +127,21 @@
name = Products;
sourceTree = "<group>";
};
9CDB1D9DB6483D893504BFCB /* Pods */ = {
isa = PBXGroup;
children = (
505CB61090817F4453631957 /* Pods-Joplin.debug.xcconfig */,
14868D674CC065C7BF1C9944 /* Pods-Joplin.release.xcconfig */,
09056573D4C040FBD5FEB93A /* Pods-Joplin-JoplinTests.debug.xcconfig */,
2C91CD1424C7137D07789148 /* Pods-Joplin-JoplinTests.release.xcconfig */,
A3FEB746EE7F1B0FF28528E1 /* Pods-Joplin-tvOS.debug.xcconfig */,
245A6EBAE2E874DB706B16DB /* Pods-Joplin-tvOS.release.xcconfig */,
2DA44D9A347489A29B995F73 /* Pods-Joplin-tvOSTests.debug.xcconfig */,
37DBC181C4AD99CBE0D07EEB /* Pods-Joplin-tvOSTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -147,19 +149,20 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Joplin" */;
buildPhases = (
071815849A41B8B427FC87DC /* [CP] Check Pods Manifest.lock */,
335ACF4DE85695BEBB18D8A3 /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
B4AFEC44AA65E5E06375BA82 /* [CP] Copy Pods Resources */,
CBC8354E4CF5CF4E15F2FCDE /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Joplin;
productName = "Hello World";
productName = Joplin;
productReference = 13B07F961A680F5B00A75B9A /* Joplin.app */;
productType = "com.apple.product-type.application";
};
@ -169,29 +172,20 @@
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 610;
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = joplinapp.org;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = A9BXAFS6CT;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Push = {
enabled = 1;
};
com.apple.iCloud = {
enabled = 1;
};
};
LastSwiftMigration = 1120;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Joplin" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@ -210,8 +204,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -232,7 +226,7 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=/usr/local/opt/node@12/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
071815849A41B8B427FC87DC /* [CP] Check Pods Manifest.lock */ = {
335ACF4DE85695BEBB18D8A3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -254,7 +248,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
B4AFEC44AA65E5E06375BA82 /* [CP] Copy Pods Resources */ = {
CBC8354E4CF5CF4E15F2FCDE /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -277,6 +271,7 @@
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
@ -296,12 +291,32 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Joplin/Pods-Joplin-resources.sh\"\n";
showEnvVarsInLog = 0;
};
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Start Packager";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -316,47 +331,19 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = Joplin;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 756FF6C78FB548317DAD12C0 /* Pods-Joplin.debug.xcconfig */;
baseConfigurationReference = 505CB61090817F4453631957 /* Pods-Joplin.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 55;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = A9BXAFS6CT;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)..",
ode_modules,
"eact-native-image-pickerios",
"$(SRCROOT)..",
ode_modules,
"eact-native-securerandomios",
"$(SRCROOT)..",
ode_modules,
"eact-native-file-viewerios",
);
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 10.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
@ -365,8 +352,8 @@
);
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin;
PRODUCT_NAME = Joplin;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
@ -374,25 +361,15 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6CFFC13990302AAE61F81DB1 /* Pods-Joplin.release.xcconfig */;
baseConfigurationReference = 14868D674CC065C7BF1C9944 /* Pods-Joplin.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 55;
DEVELOPMENT_TEAM = A9BXAFS6CT;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)..\node_modules\neact-native-image-pickerios",
"$(SRCROOT)..\node_modules\neact-native-securerandomios",
"$(SRCROOT)..\node_modules\neact-native-file-viewerios",
);
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 10.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
@ -401,8 +378,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin;
PRODUCT_NAME = Joplin;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
@ -412,24 +388,37 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@ -442,7 +431,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -453,17 +448,28 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -471,13 +477,20 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = (
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "Joplin-tvOSTests.xctest"
BlueprintName = "Joplin-tvOSTests"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,25 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@ -34,12 +20,16 @@
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "NO"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
@ -47,34 +37,13 @@
BlueprintName = "JoplinTests"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Joplin.app"
BlueprintName = "Joplin"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@ -91,8 +60,6 @@
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -7,4 +7,4 @@
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
</Workspace>

View File

@ -1,16 +1,8 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
@property (nonatomic, strong) UIWindow *window;

View File

@ -1,32 +1,82 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h"
#import "RNQuickActionManager.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
#import "RNQuickActionManager.h"
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
static void InitializeFlipper(UIApplication *application) {
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin:[FlipperKitReactPlugin new]];
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
}
#endif
@implementation AppDelegate
// ===================================================
// BEGIN react-native-quick-actions
// ===================================================
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded)) completionHandler {
[RNQuickActionManager onQuickActionPress:shortcutItem completionHandler:completionHandler];
}
// ===================================================
// END react-native-quick-actions
// ===================================================
// ===================================================
// BEGIN react-native-push-notification-ios
// ===================================================
// IOS 10+ Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
completionHandler();
}
// IOS 4-10 Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
// ===================================================
// END react-native-push-notification-ios
// ===================================================
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#ifdef FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Joplin"
initialProperties:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Joplin"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
@ -35,49 +85,15 @@
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
// BEGIN react-native-push-notification-ios
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
// END react-native-push-notification-ios
return YES;
}
// =====================================================
// For @react-native-community/push-notification-ios
// =====================================================
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
// =====================================================
// /For @react-native-community/push-notification-ios
// =====================================================
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG

View File

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Joplin" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@ -47,6 +47,8 @@
<string>To add geo-location information to a note. Can be disabled in app.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>To allow attaching images to a note</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>The images will be displayed on your notes.</string>
<key>UIAppFonts</key>
<array/>
<key>UILaunchStoryboardName</key>
@ -72,5 +74,24 @@
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
<string>Fontisto.ttf</string>
</array>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
<rect key="frame" x="0.0" y="647" width="375" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Joplin" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52.173913043478265" y="375"/>
</scene>
</scenes>
</document>

View File

@ -1,12 +1,3 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,65 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React"
@interface JoplinTests : XCTestCase
@end
@implementation JoplinTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
#ifdef DEBUG
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
#endif
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
#ifdef DEBUG
RCTSetLogFunction(RCTDefaultLogFunction);
#endif
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end

View File

@ -1,43 +1,23 @@
platform :ios, '9.0'
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'Joplin' do
# Pods for Joplin
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/'
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'rn-fetch-blob', :path => '../node_modules/rn-fetch-blob'
use_native_modules!
end
# target 'Joplin-tvOS' do
# # Pods for Joplin-tvOS
# target 'Joplin-tvOSTests' do
# inherit! :search_paths
# # Pods for testing
# end
# end
# Note: it was 13.4 to get @react-native-community/datetimepicker to work
# but it's probably not necessary actually. Just needed to upgrade XCode.
platform :ios, '10.0'
target 'Joplin' do
config = use_native_modules!
use_react_native!(:path => config["reactNativePath"])
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!
post_install do |installer|
flipper_post_install(installer)
end
end

View File

@ -1,273 +1,380 @@
PODS:
- boost-for-react-native (1.63.0)
- CocoaAsyncSocket (7.6.4)
- CocoaLibEvent (1.0.0)
- DoubleConversion (1.1.6)
- FBLazyVector (0.61.5)
- FBReactNativeSpec (0.61.5):
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.5)
- RCTTypeSafety (= 0.61.5)
- React-Core (= 0.61.5)
- React-jsi (= 0.61.5)
- ReactCommon/turbomodule/core (= 0.61.5)
- Folly (2018.10.22.00):
- FBLazyVector (0.63.3)
- FBReactNativeSpec (0.63.3):
- Folly (= 2020.01.13.00)
- RCTRequired (= 0.63.3)
- RCTTypeSafety (= 0.63.3)
- React-Core (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- Flipper (0.54.0):
- Flipper-Folly (~> 2.2)
- Flipper-RSocket (~> 1.1)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.3.0):
- boost-for-react-native
- CocoaLibEvent (~> 1.0)
- Flipper-DoubleConversion
- Flipper-Glog
- OpenSSL-Universal (= 1.0.2.20)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.1.0):
- Flipper-Folly (~> 2.2)
- FlipperKit (0.54.0):
- FlipperKit/Core (= 0.54.0)
- FlipperKit/Core (0.54.0):
- Flipper (~> 0.54.0)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- FlipperKit/CppBridge (0.54.0):
- Flipper (~> 0.54.0)
- FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
- Flipper-Folly (~> 2.2)
- FlipperKit/FBDefines (0.54.0)
- FlipperKit/FKPortForwarding (0.54.0):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- FlipperKit/FlipperKitHighlightOverlay (0.54.0)
- FlipperKit/FlipperKitLayoutPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- YogaKit (~> 1.18)
- FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
- FlipperKit/FlipperKitNetworkPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitReactPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/SKIOSNetworkPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- Folly (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- Folly/Default (= 2018.10.22.00)
- Folly/Default (= 2020.01.13.00)
- glog
- Folly/Default (2018.10.22.00):
- Folly/Default (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- glog
- glog (0.3.5)
- RCTRequired (0.61.5)
- RCTTypeSafety (0.61.5):
- FBLazyVector (= 0.61.5)
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.5)
- React-Core (= 0.61.5)
- React (0.61.5):
- React-Core (= 0.61.5)
- React-Core/DevSupport (= 0.61.5)
- React-Core/RCTWebSocket (= 0.61.5)
- React-RCTActionSheet (= 0.61.5)
- React-RCTAnimation (= 0.61.5)
- React-RCTBlob (= 0.61.5)
- React-RCTImage (= 0.61.5)
- React-RCTLinking (= 0.61.5)
- React-RCTNetwork (= 0.61.5)
- React-RCTSettings (= 0.61.5)
- React-RCTText (= 0.61.5)
- React-RCTVibration (= 0.61.5)
- React-Core (0.61.5):
- Folly (= 2018.10.22.00)
- OpenSSL-Universal (1.0.2.20):
- OpenSSL-Universal/Static (= 1.0.2.20)
- OpenSSL-Universal/Static (1.0.2.20)
- RCTRequired (0.63.3)
- RCTTypeSafety (0.63.3):
- FBLazyVector (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTRequired (= 0.63.3)
- React-Core (= 0.63.3)
- React (0.63.3):
- React-Core (= 0.63.3)
- React-Core/DevSupport (= 0.63.3)
- React-Core/RCTWebSocket (= 0.63.3)
- React-RCTActionSheet (= 0.63.3)
- React-RCTAnimation (= 0.63.3)
- React-RCTBlob (= 0.63.3)
- React-RCTImage (= 0.63.3)
- React-RCTLinking (= 0.63.3)
- React-RCTNetwork (= 0.63.3)
- React-RCTSettings (= 0.63.3)
- React-RCTText (= 0.63.3)
- React-RCTVibration (= 0.63.3)
- React-callinvoker (0.63.3)
- React-Core (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default (= 0.61.5)
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-Core/Default (= 0.63.3)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/CoreModulesHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/CoreModulesHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/Default (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/Default (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/DevSupport (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/DevSupport (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default (= 0.61.5)
- React-Core/RCTWebSocket (= 0.61.5)
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-jsinspector (= 0.61.5)
- React-Core/Default (= 0.63.3)
- React-Core/RCTWebSocket (= 0.63.3)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- React-jsinspector (= 0.63.3)
- Yoga
- React-Core/RCTActionSheetHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTActionSheetHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTAnimationHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTAnimationHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTBlobHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTBlobHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTImageHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTImageHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTLinkingHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTLinkingHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTNetworkHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTNetworkHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTSettingsHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTSettingsHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTTextHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTTextHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTVibrationHeaders (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTVibrationHeaders (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-Core/RCTWebSocket (0.61.5):
- Folly (= 2018.10.22.00)
- React-Core/RCTWebSocket (0.63.3):
- Folly (= 2020.01.13.00)
- glog
- React-Core/Default (= 0.61.5)
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsiexecutor (= 0.61.5)
- React-Core/Default (= 0.63.3)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsiexecutor (= 0.63.3)
- Yoga
- React-CoreModules (0.61.5):
- FBReactNativeSpec (= 0.61.5)
- Folly (= 2018.10.22.00)
- RCTTypeSafety (= 0.61.5)
- React-Core/CoreModulesHeaders (= 0.61.5)
- React-RCTImage (= 0.61.5)
- ReactCommon/turbomodule/core (= 0.61.5)
- React-cxxreact (0.61.5):
- React-CoreModules (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/CoreModulesHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- React-RCTImage (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-cxxreact (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- Folly (= 2020.01.13.00)
- glog
- React-jsinspector (= 0.61.5)
- React-jsi (0.61.5):
- React-callinvoker (= 0.63.3)
- React-jsinspector (= 0.63.3)
- React-jsi (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- Folly (= 2020.01.13.00)
- glog
- React-jsi/Default (= 0.61.5)
- React-jsi/Default (0.61.5):
- React-jsi/Default (= 0.63.3)
- React-jsi/Default (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- Folly (= 2020.01.13.00)
- glog
- React-jsiexecutor (0.61.5):
- React-jsiexecutor (0.63.3):
- DoubleConversion
- Folly (= 2018.10.22.00)
- Folly (= 2020.01.13.00)
- glog
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- React-jsinspector (0.61.5)
- react-native-camera (2.10.2):
- React
- react-native-camera/RCT (= 2.10.2)
- react-native-camera/RN (= 2.10.2)
- react-native-camera/RCT (2.10.2):
- React
- react-native-camera/RN (2.10.2):
- React
- react-native-document-picker (2.3.0):
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsinspector (0.63.3)
- react-native-alarm-notification (1.7.1):
- React
- react-native-camera (3.40.0):
- React-Core
- react-native-camera/RCT (= 3.40.0)
- react-native-camera/RN (= 3.40.0)
- react-native-camera/RCT (3.40.0):
- React-Core
- react-native-camera/RN (3.40.0):
- React-Core
- react-native-document-picker (4.0.0):
- React-Core
- react-native-geolocation (2.0.2):
- React
- react-native-image-picker (0.14.3):
- react-native-image-picker (2.3.4):
- React-Core
- react-native-image-resizer (1.3.0):
- React-Core
- react-native-slider (3.0.3):
- React
- react-native-image-resizer (1.0.0):
- react-native-sqlite-storage (5.0.0):
- React
- react-native-slider (2.0.8):
- React
- react-native-sqlite-storage (4.1.0):
- React
- react-native-version-info (1.0.1):
- React
- react-native-webview (5.12.0):
- React
- React-RCTActionSheet (0.61.5):
- React-Core/RCTActionSheetHeaders (= 0.61.5)
- React-RCTAnimation (0.61.5):
- React-Core/RCTAnimationHeaders (= 0.61.5)
- React-RCTBlob (0.61.5):
- React-Core/RCTBlobHeaders (= 0.61.5)
- React-Core/RCTWebSocket (= 0.61.5)
- React-jsi (= 0.61.5)
- React-RCTNetwork (= 0.61.5)
- React-RCTImage (0.61.5):
- React-Core/RCTImageHeaders (= 0.61.5)
- React-RCTNetwork (= 0.61.5)
- React-RCTLinking (0.61.5):
- React-Core/RCTLinkingHeaders (= 0.61.5)
- React-RCTNetwork (0.61.5):
- React-Core/RCTNetworkHeaders (= 0.61.5)
- React-RCTSettings (0.61.5):
- React-Core/RCTSettingsHeaders (= 0.61.5)
- React-RCTText (0.61.5):
- React-Core/RCTTextHeaders (= 0.61.5)
- React-RCTVibration (0.61.5):
- React-Core/RCTVibrationHeaders (= 0.61.5)
- ReactCommon/jscallinvoker (0.61.5):
- react-native-version-info (1.1.0):
- React-Core
- react-native-webview (10.9.2):
- React-Core
- React-RCTActionSheet (0.63.3):
- React-Core/RCTActionSheetHeaders (= 0.63.3)
- React-RCTAnimation (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTAnimationHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTBlob (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- React-Core/RCTBlobHeaders (= 0.63.3)
- React-Core/RCTWebSocket (= 0.63.3)
- React-jsi (= 0.63.3)
- React-RCTNetwork (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTImage (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTImageHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- React-RCTNetwork (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTLinking (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- React-Core/RCTLinkingHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTNetwork (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTNetworkHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTSettings (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- RCTTypeSafety (= 0.63.3)
- React-Core/RCTSettingsHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- React-RCTText (0.63.3):
- React-Core/RCTTextHeaders (= 0.63.3)
- React-RCTVibration (0.63.3):
- FBReactNativeSpec (= 0.63.3)
- Folly (= 2020.01.13.00)
- React-Core/RCTVibrationHeaders (= 0.63.3)
- React-jsi (= 0.63.3)
- ReactCommon/turbomodule/core (= 0.63.3)
- ReactCommon/turbomodule/core (0.63.3):
- DoubleConversion
- Folly (= 2018.10.22.00)
- Folly (= 2020.01.13.00)
- glog
- React-cxxreact (= 0.61.5)
- ReactCommon/turbomodule/core (0.61.5):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-Core (= 0.61.5)
- React-cxxreact (= 0.61.5)
- React-jsi (= 0.61.5)
- ReactCommon/jscallinvoker (= 0.61.5)
- React-callinvoker (= 0.63.3)
- React-Core (= 0.63.3)
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- rn-fetch-blob (0.12.0):
- React-Core
- RNCPushNotificationIOS (1.0.5):
- React
- RNDeviceInfo (5.5.1):
- React
- RNFileViewer (1.0.1):
- React
- RNFS (2.11.17):
- RNCClipboard (1.5.0):
- React-Core
- RNCPushNotificationIOS (1.6.0):
- React-Core
- RNDateTimePicker (3.0.3):
- React-Core
- RNFileViewer (2.1.4):
- React-Core
- RNFS (2.16.6):
- React
- RNQuickAction (0.3.13):
- React
- RNSecureRandom (1.0.0-rc.0):
- React
- RNVectorIcons (6.6.0):
- RNShare (4.0.2):
- React-Core
- RNVectorIcons (7.1.0):
- React
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
- Flipper (~> 0.54.0)
- Flipper-DoubleConversion (= 1.1.7)
- Flipper-Folly (~> 2.2)
- Flipper-Glog (= 0.3.6)
- Flipper-PeerTalk (~> 0.0.4)
- Flipper-RSocket (~> 1.1)
- FlipperKit (~> 0.54.0)
- FlipperKit/Core (~> 0.54.0)
- FlipperKit/CppBridge (~> 0.54.0)
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0)
- FlipperKit/FBDefines (~> 0.54.0)
- FlipperKit/FKPortForwarding (~> 0.54.0)
- FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0)
- FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0)
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0)
- FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0)
- FlipperKit/FlipperKitReactPlugin (~> 0.54.0)
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0)
- FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
@ -276,6 +383,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-alarm-notification (from `../node_modules/react-native-alarm-notification`)
- react-native-camera (from `../node_modules/react-native-camera`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
@ -294,21 +402,33 @@ DEPENDENCIES:
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/jscallinvoker (from `../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)"
- "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNFileViewer (from `../node_modules/react-native-file-viewer/ios`)
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- RNFileViewer (from `../node_modules/react-native-file-viewer`)
- RNFS (from `../node_modules/react-native-fs`)
- RNQuickAction (from `../node_modules/react-native-quick-actions`)
- RNSecureRandom (from `../node_modules/react-native-securerandom`)
- RNShare (from `../node_modules/react-native-share`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
trunk:
- boost-for-react-native
- CocoaAsyncSocket
- CocoaLibEvent
- Flipper
- Flipper-DoubleConversion
- Flipper-Folly
- Flipper-Glog
- Flipper-PeerTalk
- Flipper-RSocket
- FlipperKit
- OpenSSL-Universal
- YogaKit
EXTERNAL SOURCES:
DoubleConversion:
@ -327,6 +447,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../node_modules/react-native/"
React-callinvoker:
:path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Core:
:path: "../node_modules/react-native/"
React-CoreModules:
@ -339,6 +461,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-alarm-notification:
:path: "../node_modules/react-native-alarm-notification"
react-native-camera:
:path: "../node_modules/react-native-camera"
react-native-document-picker:
@ -379,18 +503,22 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
rn-fetch-blob:
:path: "../node_modules/rn-fetch-blob"
RNCClipboard:
:path: "../node_modules/@react-native-community/clipboard"
RNCPushNotificationIOS:
:path: "../node_modules/@react-native-community/push-notification-ios"
RNDeviceInfo:
:path: "../node_modules/react-native-device-info"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNFileViewer:
:path: "../node_modules/react-native-file-viewer/ios"
:path: "../node_modules/react-native-file-viewer"
RNFS:
:path: "../node_modules/react-native-fs"
RNQuickAction:
:path: "../node_modules/react-native-quick-actions"
RNSecureRandom:
:path: "../node_modules/react-native-securerandom"
RNShare:
:path: "../node_modules/react-native-share"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
Yoga:
@ -398,49 +526,64 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
DoubleConversion: cde416483dac037923206447da6e1454df403714
FBLazyVector: aaeaf388755e4f29cd74acbc9e3b8da6d807c37f
FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78
React-Core: 688b451f7d616cc1134ac95295b593d1b5158a04
React-CoreModules: d04f8494c1a328b69ec11db9d1137d667f916dcb
React-cxxreact: d0f7bcafa196ae410e5300736b424455e7fb7ba7
React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
react-native-camera: ea85f74cf2e501d2a91a3da7092ecdac082f18df
react-native-document-picker: 8508bfda34be6c24a2cbecbe515b15108658c183
OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
React: f36e90f3ceb976546e97df3403e37d226f79d0e3
React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd
React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6
React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1
React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99
React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
react-native-alarm-notification: 8f2adb4521bf7ca867278d4c236553e154dbbbc6
react-native-camera: 5c1fbfecf63b802b8ca4a71c60d30a71550fb348
react-native-document-picker: b3e78a8f7fef98b5cb069f20fc35797d55e68e28
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
react-native-image-picker: 3693786b3d5958c8f71deed66ec068b323565e0d
react-native-image-resizer: aa1600566fd336a044daf9273dcec5033c1d41ca
react-native-slider: b2f361499888302147205f17f6fffa921a7bda70
react-native-sqlite-storage: bb10beb2407e5fc21f3f1bcd86bacbfd6edcc818
react-native-version-info: daadd78b8fc93cf2764cb5aa52ec995961b62201
react-native-webview: 26d8993b090ca5d59eb9a9466dcb8291baa3223e
React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
React-RCTImage: 6b8e8df449eb7c814c99a92d6b52de6fe39dea4e
React-RCTLinking: 121bb231c7503cf9094f4d8461b96a130fabf4a5
React-RCTNetwork: fb353640aafcee84ca8b78957297bd395f065c9a
React-RCTSettings: 8db258ea2a5efee381fcf7a6d5044e2f8b68b640
React-RCTText: 9ccc88273e9a3aacff5094d2175a605efa854dbe
React-RCTVibration: a49a1f42bf8f5acf1c3e297097517c6b3af377ad
ReactCommon: 198c7c8d3591f975e5431bec1b0b3b581aa1c5dd
react-native-image-picker: 32d1ad2c0024ca36161ae0d5c2117e2d6c441f11
react-native-image-resizer: b53bf95ad880100e20262687e41f76fdbc9df255
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
react-native-sqlite-storage: ce71689c5a73b79390a1ab213555ae80979a5dc7
react-native-version-info: 64f0f0bf3da6316298f9cd6085d50ba3a992d0c7
react-native-webview: c51f73be304c61d359ec3e7c5e4e8f2c977fd360
React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa
React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2
React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40
React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387
React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18
React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda
React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773
React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCPushNotificationIOS: 30ed75aa2981a4ee00b8a2c46599ff18a9c7fcf6
RNDeviceInfo: 6812ed7e578d4344bf4e77102f014b5adf0b5c28
RNFileViewer: 5047ecf40477339723cc08abd55b5ebcefcef4b5
RNFS: 416676c3a9ae404454bade10e3d78147c7c33a40
RNCClipboard: 8f9f12fabf3c06e976f19f87a62c89e28dfedfca
RNCPushNotificationIOS: 20c4403b2ef8732297ea81e22f66c41bed7aaedf
RNDateTimePicker: e9fcd5ecdc0c5b018871e0d178d6040dca11973c
RNFileViewer: 83cc066ad795b1f986791d03b56fe0ee14b6a69f
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNSecureRandom: 1f19ad1492f7ed416b8fc79e92216a1f73f13a4c
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
RNShare: 7a7277f3c313652422d9de072ac50714dff5e8a4
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: c8797581a23c0ae9c6a4f471c2b19dda3644a8b2
PODFILE CHECKSUM: a0d1ca4e385ef46f9103f02206ebf612107dd508
COCOAPODS: 1.9.3

View File

@ -698,6 +698,7 @@ export default class BaseApplication {
initArgs = Object.assign(initArgs, extraFlags);
this.logger_.addTarget(TargetType.File, { path: `${profileDir}/log.txt` });
// this.logger_.addTarget(TargetType.Console, { level: Logger.LEVEL_DEBUG });
this.logger_.setLevel(initArgs.logLevel);
reg.setLogger(this.logger_);

View File

@ -0,0 +1,84 @@
// There are plenty of packages for in-memory caching but each have their
// own gotchas and often have extra complexity which makes it work in one
// platform but not in another (for example, the use of long timeouts would
// be fine in Node but not in React Native).
//
// So this class implements a simple in-memory cache with support for TTL.
// Checking for expired keys is a bit inefficient since it doesn't rely on
// timers, so it's checking every time a value is set or retrieved. But it
// should be fine in most cases, as long as the class is not used at a massive
// scale.
interface Record {
value: any,
expiredTime: number,
}
interface Records {
[key:string]: Record;
}
interface ExpirableKeys {
[key:string]: boolean,
}
export default class Cache {
private maxRecords_:number;
private records_:Records = {};
private expirableKeys_:ExpirableKeys = {};
private recordKeyHistory_:string[] = [];
constructor(maxRecords:number = 50) {
this.maxRecords_ = maxRecords;
}
private checkExpiredRecords() {
const now = Date.now();
for (const key in this.expirableKeys_) {
if (!this.records_[key]) {
delete this.expirableKeys_[key];
} else {
if (this.records_[key].expiredTime <= now) {
delete this.records_[key];
delete this.expirableKeys_[key];
}
}
}
while (this.recordKeyHistory_.length > this.maxRecords_) {
const key = this.recordKeyHistory_[0];
delete this.records_[key];
delete this.expirableKeys_[key];
this.recordKeyHistory_.splice(0, 1);
}
}
public value(key:string, defaultValue:any = undefined):any {
this.checkExpiredRecords();
if (key in this.records_) return this.records_[key].value;
return defaultValue;
}
public setValue(key:string, value:any, ttl:number = 0) {
this.checkExpiredRecords();
this.records_[key] = {
value: value,
expiredTime: ttl ? Date.now() + ttl : 0,
};
const idx = this.recordKeyHistory_.indexOf(key);
if (idx >= 0) this.recordKeyHistory_.splice(idx, 1);
this.recordKeyHistory_.push(key);
if (ttl) {
this.expirableKeys_[key] = true;
} else {
delete this.expirableKeys_[key];
}
}
}

View File

@ -0,0 +1,102 @@
// On mobile all the setTimeout and setInterval should go through this class
// as it will either use the native timeout/interval for short intervals or
// the custom one for long intervals.
// For custom intervals, they are triggered
// whenever the update() function is called, and in mobile it's called for
// example on the Redux action middleware or when the app gets focus.
const { time } = require('lib/time-utils.js');
type IntervalId = number;
interface Interval {
id: IntervalId,
callback: Function,
interval: number,
lastIntervalTime: number,
isTimeout: boolean,
}
interface Intervals {
[key: number]: Interval;
}
export default class PoorManIntervals {
private static maxNativeTimerDuration_ = 10 * 1000;
private static lastUpdateTime_:number = 0;
private static intervalId_:IntervalId = 0;
private static intervals_:Intervals = {};
public static setInterval(callback:Function, interval:number):IntervalId {
if (interval <= this.maxNativeTimerDuration_) return setInterval(callback, interval);
this.intervalId_++;
const id = this.intervalId_;
this.intervals_[id] = {
id: id,
callback: callback,
interval: interval,
lastIntervalTime: time.unixMs(),
isTimeout: false,
};
return id;
}
public static setTimeout(callback:Function, interval:number):IntervalId {
if (interval <= this.maxNativeTimerDuration_) return setTimeout(callback, interval);
this.intervalId_++;
const id = this.intervalId_;
this.intervals_[id] = {
id: id,
callback: callback,
interval: interval,
lastIntervalTime: time.unixMs(),
isTimeout: true,
};
return id;
}
public static clearInterval(id:IntervalId) {
const r = this.intervals_[id];
if (!r) {
clearInterval(id);
} else {
delete this.intervals_[id];
}
}
public static clearTimeout(id:IntervalId) {
const r = this.intervals_[id];
if (!r) {
clearTimeout(id);
} else {
delete this.intervals_[id];
}
}
public static update() {
// Don't update more than once a second
if (this.lastUpdateTime_ + 1000 > time.unixMs()) return;
for (const id in this.intervals_) {
const interval = this.intervals_[id];
const now = time.unixMs();
if (now - interval.lastIntervalTime >= interval.interval) {
interval.lastIntervalTime = now;
interval.callback();
if (interval.isTimeout) {
this.clearTimeout(interval.id);
}
}
}
this.lastUpdateTime_ = time.unixMs();
}
}

View File

@ -335,8 +335,9 @@ class WebDavApi {
// </d:propfind>'
async exec(method, path = '', body = null, headers = null, options = null) {
if (headers === null) headers = {};
if (options === null) options = {};
headers = Object.assign({}, headers);
options = Object.assign({}, options);
if (!options.responseFormat) options.responseFormat = 'json';
if (!options.target) options.target = 'string';
@ -344,6 +345,13 @@ class WebDavApi {
if (authToken) headers['Authorization'] = `Basic ${authToken}`;
// That should not be needed, but it is required for React Native 0.63+
// https://github.com/facebook/react-native/issues/30176
if (!headers['Content-Type']) {
if (method === 'PROPFIND') headers['Content-Type'] = 'text/xml';
if (method === 'PUT') headers['Content-Type'] = 'text/plain';
}
// On iOS, the network lib appends a If-None-Match header to PROPFIND calls, which is kind of correct because
// the call is idempotent and thus could be cached. According to RFC-7232 though only GET and HEAD should have
// this header for caching purposes. It makes no mention of PROPFIND.
@ -365,7 +373,6 @@ class WebDavApi {
if (shim.httpAgent(url)) fetchOptions.agent = shim.httpAgent(url);
let response = null;
// console.info('WebDAV Call', `${method} ${url}`, headers, options);

View File

@ -3,7 +3,7 @@ const { Platform, PermissionsAndroid } = require('react-native');
type rationale = {
title: string,
message: string,
buttonPositive: string,
buttonPositive?: string,
buttonNegative?: string
buttonNeutral?: string
}
@ -15,5 +15,5 @@ export default async (permissions: string, rationale?: rationale) => {
if (result !== PermissionsAndroid.RESULTS.GRANTED) {
result = await PermissionsAndroid.request(permissions, rationale);
}
return result === PermissionsAndroid.RESULTS.GRANTED;
return result;
};

View File

@ -1,4 +1,4 @@
import { RNCamera } from 'react-native-camera';
const { RNCamera } = require('react-native-camera');
const React = require('react');
const Component = React.Component;
const { connect } = require('react-redux');
@ -31,7 +31,7 @@ class CameraView extends Component {
this.onLayout = this.onLayout.bind(this);
}
onLayout(event) {
onLayout(event:any) {
this.setState({
screenWidth: event.nativeEvent.layout.width,
screenHeight: event.nativeEvent.layout.height,
@ -70,23 +70,26 @@ class CameraView extends Component {
fixOrientation: true,
});
this.setState({ snapping: false });
if (this.props.onPhoto) this.props.onPhoto(result);
this.setState({ snapping: false });
}
async onCameraReady() {
const ratios = await this.camera.getSupportedRatiosAsync();
this.setState({ ratios: ratios });
if (this.supportsRatios()) {
const ratios = await this.camera.getSupportedRatiosAsync();
this.setState({ ratios: ratios });
}
}
renderButton(onPress, iconName, style) {
renderButton(onPress:Function, iconNameOrIcon:any, style:any) {
let icon = null;
if (typeof iconName === 'string') {
if (typeof iconNameOrIcon === 'string') {
icon = (
<Icon
name={iconName}
name={iconNameOrIcon}
style={{
fontSize: 40,
color: 'black',
@ -94,7 +97,7 @@ class CameraView extends Component {
/>
);
} else {
icon = iconName;
icon = iconNameOrIcon;
}
return (
@ -106,11 +109,11 @@ class CameraView extends Component {
);
}
fitRectIntoBounds(rect, bounds) {
fitRectIntoBounds(rect:any, bounds:any) {
const rectRatio = rect.width / rect.height;
const boundsRatio = bounds.width / bounds.height;
const newDimensions = {};
const newDimensions:any = {};
// Rect is more landscape than bounds - fit to width
if (rectRatio > boundsRatio) {
@ -124,7 +127,7 @@ class CameraView extends Component {
return newDimensions;
}
cameraRect(ratio) {
cameraRect(ratio:string) {
// To keep the calculations simpler, it's assumed that the phone is in
// portrait orientation. Then at the end we swap the values if needed.
const splitted = ratio.split(':');
@ -146,16 +149,21 @@ class CameraView extends Component {
return output;
}
supportsRatios() {
return shim.mobilePlatform() === 'android';
}
render() {
const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera';
const displayRatios = shim.mobilePlatform() === 'android' && this.state.ratios.length > 1;
const displayRatios = this.supportsRatios() && this.state.ratios.length > 1;
const reverseCameraButton = this.renderButton(this.reverse_onPress, 'md-reverse-camera', { flex: 1, flexDirection: 'row', justifyContent: 'flex-start', marginLeft: 20 });
const reverseCameraButton = this.renderButton(this.reverse_onPress, 'md-camera-reverse', { flex: 1, flexDirection: 'row', justifyContent: 'flex-start', marginLeft: 20 });
const ratioButton = !displayRatios ? <View style={{ flex: 1 }}/> : this.renderButton(this.ratio_onPress, <Text style={{ fontWeight: 'bold', fontSize: 20 }}>{Setting.value('camera.ratio')}</Text>, { flex: 1, flexDirection: 'row', justifyContent: 'flex-end', marginRight: 20 });
let cameraRatio = '4:3';
const cameraProps = {};
const cameraProps:any = {};
if (displayRatios) {
cameraProps.ratio = this.props.cameraRatio;
cameraRatio = this.props.cameraRatio;
@ -170,7 +178,7 @@ class CameraView extends Component {
<View style={{ position: 'absolute', backgroundColor: '#000000', width: '100%', height: '100%' }}/>
<RNCamera
style={Object.assign({ position: 'absolute' }, cameraRect)}
ref={ref => {
ref={(ref:any) => {
this.camera = ref;
}}
type={this.props.cameraType}
@ -223,7 +231,7 @@ class CameraView extends Component {
}
}
const mapStateToProps = state => {
const mapStateToProps = (state:any) => {
return {
cameraRatio: state.settings['camera.ratio'],
cameraType: state.settings['camera.type'],
@ -231,4 +239,4 @@ const mapStateToProps = state => {
};
module.exports = connect(mapStateToProps)(CameraView);
export default connect(mapStateToProps)(CameraView);

View File

@ -0,0 +1,111 @@
import { useRef, useMemo, useCallback } from 'react';
import Setting from 'lib/models/Setting';
import useSource from './hooks/useSource';
import useOnMessage from './hooks/useOnMessage';
import useOnResourceLongPress from './hooks/useOnResourceLongPress';
const React = require('react');
const { View } = require('react-native');
const { WebView } = require('react-native-webview');
const { themeStyle } = require('lib/components/global-style.js');
const BackButtonDialogBox = require('lib/components/BackButtonDialogBox').default;
const { reg } = require('lib/registry.js');
interface Props {
themeId: number,
style: any,
noteBody: string,
noteMarkupLanguage: number,
highlightedKeywords: string[],
noteResources: any,
paddingBottom: number,
noteHash: string,
onJoplinLinkClick: Function,
onCheckboxChange?: Function,
onMarkForDownload?: Function,
onLoadEnd?: Function,
}
export default function NoteBodyViewer(props:Props) {
const theme = themeStyle(props.themeId);
const webViewStyle:any = useMemo(() => {
return { backgroundColor: theme.backgroundColor };
}, [theme.backgroundColor]);
const dialogBoxRef = useRef(null);
const { source, injectedJs } = useSource(
props.noteBody,
props.noteMarkupLanguage,
props.themeId,
props.highlightedKeywords,
props.noteResources,
props.paddingBottom,
props.noteHash
);
const onResourceLongPress = useOnResourceLongPress(
props.onJoplinLinkClick,
dialogBoxRef
);
const onMessage = useOnMessage(
props.onCheckboxChange,
props.noteBody,
props.onMarkForDownload,
props.onJoplinLinkClick,
onResourceLongPress
);
const onLoadEnd = useCallback(() => {
if (props.onLoadEnd) props.onLoadEnd();
}, [props.onLoadEnd]);
function onError() {
reg.logger().error('WebView error');
}
// On iOS scalesPageToFit work like this:
//
// Find the widest image, resize it *and everything else* by x% so that
// the image fits within the viewport. The problem is that it means if there's
// a large image, everything is going to be scaled to a very small size, making
// the text unreadable.
//
// On Android:
//
// Find the widest elements and scale them (and them only) to fit within the viewport
// It means it's going to scale large images, but the text will remain at the normal
// size.
//
// That means we can use scalesPageToFix on Android but not on iOS.
// The weird thing is that on iOS, scalesPageToFix=false along with a CSS
// rule "img { max-width: 100% }", works like scalesPageToFix=true on Android.
// So we use scalesPageToFix=false on iOS along with that CSS rule.
//
// 2020-10-15: As we've now fully switched to WebKit for iOS (useWebKit=true) and
// since the WebView package went through many versions it's possible that
// the above no longer applies.
return (
<View style={props.style}>
<WebView
theme={theme}
useWebKit={true}
allowingReadAccessToURL={`file://${Setting.value('resourceDir')}`}
style={webViewStyle}
source={source}
injectedJavaScript={injectedJs.join('\n')}
originWhitelist={['file://*', './*', 'http://*', 'https://*']}
mixedContentMode="always"
allowFileAccess={true}
onLoadEnd={onLoadEnd}
onError={onError}
onMessage={onMessage}
/>
<BackButtonDialogBox ref={dialogBoxRef}/>
</View>
);
}

View File

@ -0,0 +1,26 @@
import { useCallback } from 'react';
const shared = require('lib/components/shared/note-screen-shared');
export default function useOnMessage(onCheckboxChange:Function, noteBody:string, onMarkForDownload:Function, onJoplinLinkClick:Function, onResourceLongPress:Function) {
return useCallback((event:any) => {
// Since RN 58 (or 59) messages are now escaped twice???
const msg = unescape(unescape(event.nativeEvent.data));
console.info('Got IPC message: ', msg);
if (msg.indexOf('checkboxclick:') === 0) {
const newBody = shared.toggleCheckbox(msg, noteBody);
if (onCheckboxChange) onCheckboxChange(newBody);
} else if (msg.indexOf('markForDownload:') === 0) {
const splittedMsg = msg.split(':');
const resourceId = splittedMsg[1];
if (onMarkForDownload) onMarkForDownload({ resourceId: resourceId });
} else if (msg.startsWith('longclick:')) {
onResourceLongPress(msg);
} else if (msg.startsWith('joplin:')) {
onJoplinLinkClick(msg);
} else if (msg.startsWith('error:')) {
console.error(`Webview injected script error: ${msg}`);
}
}, [onCheckboxChange, noteBody, onMarkForDownload, onJoplinLinkClick, onResourceLongPress]);
}

View File

@ -0,0 +1,48 @@
import { useCallback } from 'react';
import Setting from 'lib/models/Setting';
import shim from 'lib/shim';
const { ToastAndroid } = require('react-native');
const { _ } = require('lib/locale.js');
const { reg } = require('lib/registry.js');
const { dialogs } = require('lib/dialogs.js');
const Resource = require('lib/models/Resource.js');
const Share = require('react-native-share').default;
export default function useOnResourceLongPress(onJoplinLinkClick:Function, dialogBoxRef:any) {
return useCallback(async (msg:string) => {
try {
const resourceId = msg.split(':')[1];
const resource = await Resource.load(resourceId);
const name = resource.title ? resource.title : resource.file_name;
const action = await dialogs.pop({ dialogbox: dialogBoxRef.current }, name, [
{ text: _('Open'), id: 'open' },
{ text: _('Share'), id: 'share' },
]);
if (action === 'open') {
onJoplinLinkClick(`joplin://${resourceId}`);
} else if (action === 'share') {
const filename = resource.file_name ?
`${resource.file_name}.${resource.file_extension}` :
resource.title;
const targetPath = `${Setting.value('resourceDir')}/${filename}`;
await shim.fsDriver().copy(Resource.fullPath(resource), targetPath);
await Share.open({
type: resource.mime,
filename: resource.title,
url: `file://${targetPath}`,
failOnCancel: false,
});
await shim.fsDriver().remove(targetPath);
}
} catch (e) {
reg.logger().error('Could not handle link long press', e);
ToastAndroid.show('An error occurred, check log for details', ToastAndroid.SHORT);
}
}, [onJoplinLinkClick]);
}

View File

@ -0,0 +1,165 @@
import { useEffect, useState, useMemo } from 'react';
import shim from 'lib/shim';
import Setting from 'lib/models/Setting';
const { themeStyle } = require('lib/components/global-style.js');
const markupLanguageUtils = require('lib/markupLanguageUtils');
const { assetsToHeaders } = require('lib/joplin-renderer');
interface Source {
uri: string,
baseUrl: string,
}
interface UseSourceResult {
source: Source,
injectedJs: string[],
}
let markupToHtml_:any = null;
function markupToHtml() {
if (markupToHtml_) return markupToHtml_;
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return markupToHtml_;
}
export default function useSource(noteBody:string, noteMarkupLanguage:number, themeId:number, highlightedKeywords:string[], noteResources:any, paddingBottom:number, noteHash:string):UseSourceResult {
const [source, setSource] = useState<Source>(undefined);
const [injectedJs, setInjectedJs] = useState<string[]>([]);
const [resourceLoadedTime, setResourceLoadedTime] = useState(0);
const [isFirstRender, setIsFirstRender] = useState(true);
const rendererTheme = useMemo(() => {
return {
bodyPaddingTop: '.8em', // Extra top padding on the rendered MD so it doesn't touch the border
bodyPaddingBottom: paddingBottom, // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
...themeStyle(themeId),
};
}, [themeId, paddingBottom]);
useEffect(() => {
let cancelled = false;
async function renderNote() {
const theme = themeStyle(themeId);
const bodyToRender = noteBody || '';
const mdOptions = {
onResourceLoaded: () => {
setResourceLoadedTime(Date.now());
},
highlightedKeywords: highlightedKeywords,
resources: noteResources,
codeTheme: theme.codeThemeCss,
postMessageSyntax: 'window.joplinPostMessage_',
enableLongPress: shim.mobilePlatform() === 'android', // On iOS, there's already a built-on open/share menu
longPressDelay: 500, // TODO use system value
};
// Whenever a resource state changes, for example when it goes from "not downloaded" to "downloaded", the "noteResources"
// props changes, thus triggering a render. The **content** of this noteResources array however is not changed because
// it doesn't contain info about the resource download state. Because of that, if we were to use the markupToHtml() cache
// it wouldn't re-render at all. We don't need this cache in any way because this hook is only triggered when we know
// something has changed.
markupToHtml().clearCache(noteMarkupLanguage);
const result = await markupToHtml().render(
noteMarkupLanguage,
bodyToRender,
rendererTheme,
mdOptions
);
if (cancelled) return;
let html = result.html;
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
const js = [];
js.push('try {');
js.push(shim.injectedJs('webviewLib'));
// Note that this postMessage function accepts two arguments, for compatibility with the desktop version, but
// the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app).
js.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
js.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
js.push(`
const readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
const hash = "${noteHash}";
// Gives it a bit of time before scrolling to the anchor
// so that images are loaded.
if (hash) {
setTimeout(() => {
const e = document.getElementById(hash);
if (!e) {
console.warn('Cannot find hash', hash);
return;
}
e.scrollIntoView();
}, 500);
}
}
}, 10);
`);
js.push('} catch (e) {');
js.push(' window.ReactNativeWebView.postMessage("error:" + e.message + ": " + JSON.stringify(e))');
js.push(' true;');
js.push('}');
js.push('true;');
html =
`
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
${assetsToHeaders(result.pluginAssets, { asHtml: true })}
</head>
<body>
${html}
</body>
</html>
`;
const tempFile = `${Setting.value('resourceDir')}/NoteBodyViewer.html`;
await shim.fsDriver().writeFile(tempFile, html, 'utf8');
if (cancelled) return;
// Now that we are sending back a file instead of an HTML string, we're always sending back the
// same file. So we add a cache busting query parameter to it, to make sure that the WebView re-renders.
//
// `baseUrl` is where the images will be loaded from. So images must use a path relative to resourceDir.
setSource({
uri: `file://${tempFile}?r=${Math.round(Math.random() * 100000000)}`,
baseUrl: `file://${Setting.value('resourceDir')}/`,
});
setInjectedJs(js);
}
// When mounted, we need to render the webview in two stages;
// - First without any source, so that all webview props are setup properly
// - Secondly with the source to actually render the note
// This is necessary to prevent a race condition that could cause an ERR_ACCESS_DENIED error
// https://github.com/react-native-webview/react-native-webview/issues/656#issuecomment-551312436
if (isFirstRender) {
setIsFirstRender(false);
setSource(undefined);
setInjectedJs([]);
} else {
renderNote();
}
return () => {
cancelled = true;
};
}, [resourceLoadedTime, noteBody, noteMarkupLanguage, themeId, rendererTheme, highlightedKeywords, noteResources, noteHash, isFirstRender]);
return { source, injectedJs };
}

View File

@ -0,0 +1,125 @@
import * as React from 'react';
import { View, Button, Text } from 'react-native';
import { themeStyle } from 'lib/theme';
import { _ } from 'lib/locale';
const PopupDialog = require('react-native-popup-dialog').default;
const { DialogTitle, DialogButton } = require('react-native-popup-dialog');
const { time } = require('lib/time-utils.js');
const DateTimePickerModal = require('react-native-modal-datetime-picker').default;
export default class SelectDateTimeDialog extends React.PureComponent<any, any> {
private dialog_:any = null;
private shown_:boolean = false;
constructor(props:any) {
super(props);
this.state = {
date: null,
mode: 'date',
showPicker: false,
};
this.onReject = this.onReject.bind(this);
this.onPickerConfirm = this.onPickerConfirm.bind(this);
this.onPickerCancel = this.onPickerCancel.bind(this);
this.onSetDate = this.onSetDate.bind(this);
}
UNSAFE_componentWillReceiveProps(newProps:any) {
if (newProps.date != this.state.date) {
this.setState({ date: newProps.date });
}
if ('shown' in newProps && newProps.shown != this.shown_) {
this.show(newProps.shown);
}
}
show(doShow:boolean = true) {
if (doShow) {
this.dialog_.show();
} else {
this.dialog_.dismiss();
}
this.shown_ = doShow;
}
dismiss() {
this.show(false);
}
onAccept() {
if (this.props.onAccept) this.props.onAccept(this.state.date);
}
onReject() {
if (this.props.onReject) this.props.onReject();
}
onClear() {
if (this.props.onAccept) this.props.onAccept(null);
}
onPickerConfirm(selectedDate:Date) {
this.setState({ date: selectedDate, showPicker: false });
}
onPickerCancel() {
this.setState({ showPicker: false });
}
onSetDate() {
this.setState({ showPicker: true });
}
renderContent() {
if (!this.shown_) return <View/>;
const theme = themeStyle(this.props.themeId);
return (
<View style={{ flex: 1, margin: 20, alignItems: 'center' }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{ this.state.date && <Text style={{ ...theme.normalText, marginRight: 10 }}>{time.formatDateToLocal(this.state.date)}</Text> }
<Button title="Set date" onPress={this.onSetDate} />
</View>
<DateTimePickerModal
date={this.state.date ? this.state.date : new Date()}
is24Hour={time.use24HourFormat()}
isVisible={this.state.showPicker}
mode="datetime"
onConfirm={this.onPickerConfirm}
onCancel={this.onPickerCancel}
/>
</View>
);
}
render() {
const clearAlarmText = _('Clear alarm'); // For unknown reasons, this particular string doesn't get translated if it's directly in the text property below
const popupActions = [
<DialogButton text={_('Save alarm')} align="center" onPress={() => this.onAccept()} key="saveButton" />,
<DialogButton text={clearAlarmText} align="center" onPress={() => this.onClear()} key="clearButton" />,
<DialogButton text={_('Cancel')} align="center" onPress={() => this.onReject()} key="cancelButton" />,
];
return (
<PopupDialog
ref={(dialog:any) => { this.dialog_ = dialog; }}
dialogTitle={<DialogTitle title={_('Set alarm')} />}
actions={popupActions}
dismissOnTouchOutside={false}
width={0.9}
height={350}
>
{this.renderContent()}
</PopupDialog>
);
}
}

View File

@ -26,6 +26,9 @@ class ActionButtonComponent extends React.Component {
this.state = {
buttonIndex: 0,
};
this.renderIconMultiStates = this.renderIconMultiStates.bind(this);
this.renderIcon = this.renderIcon.bind(this);
}
UNSAFE_componentWillReceiveProps(newProps) {
@ -55,6 +58,16 @@ class ActionButtonComponent extends React.Component {
this.newNoteNavigate(this.props.parentFolderId, false);
}
renderIconMultiStates() {
const button = this.props.buttons[this.state.buttonIndex];
return <Icon name={button.icon} style={styles.actionButtonIcon} />;
}
renderIcon() {
const mainButton = this.props.mainButton ? this.props.mainButton : {};
return mainButton.icon ? <Icon name={mainButton.icon} style={styles.actionButtonIcon} /> : <Icon name="md-add" style={styles.actionButtonIcon} />;
}
render() {
const buttons = this.props.buttons ? this.props.buttons : [];
@ -96,17 +109,13 @@ class ActionButtonComponent extends React.Component {
return null;
}
const mainButton = this.props.mainButton ? this.props.mainButton : {};
const mainIcon = mainButton.icon ? <Icon name={mainButton.icon} style={styles.actionButtonIcon} /> : <Icon name="md-add" style={styles.actionButtonIcon} />;
if (this.props.multiStates) {
if (!this.props.buttons || !this.props.buttons.length) throw new Error('Multi-state button requires at least one state');
if (this.state.buttonIndex < 0 || this.state.buttonIndex >= this.props.buttons.length) throw new Error(`Button index out of bounds: ${this.state.buttonIndex}/${this.props.buttons.length}`);
const button = this.props.buttons[this.state.buttonIndex];
const mainIcon = <Icon name={button.icon} style={styles.actionButtonIcon} />;
return (
<ReactNativeActionButton
icon={mainIcon}
renderIcon={this.renderIconMultiStates}
buttonColor="rgba(231,76,60,1)"
onPress={() => {
button.onPress();
@ -115,7 +124,7 @@ class ActionButtonComponent extends React.Component {
);
} else {
return (
<ReactNativeActionButton textStyle={styles.itemText} icon={mainIcon} buttonColor="rgba(231,76,60,1)" onPress={function() {}}>
<ReactNativeActionButton textStyle={styles.itemText} renderIcon={this.renderIcon} buttonColor="rgba(231,76,60,1)" onPress={function() {}}>
{buttonComps}
</ReactNativeActionButton>
);

View File

@ -1,322 +0,0 @@
import Async from 'react-async';
const React = require('react');
const Component = React.Component;
const { Platform, View, Text, ToastAndroid } = require('react-native');
const { WebView } = require('react-native-webview');
const { themeStyle } = require('lib/components/global-style.js');
const Setting = require('lib/models/Setting').default;
const { _ } = require('lib/locale.js');
const { reg } = require('lib/registry.js');
const shim = require('lib/shim').default;
const { assetsToHeaders } = require('lib/joplin-renderer');
const shared = require('lib/components/shared/note-screen-shared.js');
const markupLanguageUtils = require('lib/markupLanguageUtils');
const { dialogs } = require('lib/dialogs.js');
const BackButtonDialogBox = require('lib/components/BackButtonDialogBox').default;
const Resource = require('lib/models/Resource.js');
const Share = require('react-native-share').default;
class NoteBodyViewer extends Component {
constructor() {
super();
this.state = {
resources: {},
webViewLoaded: false,
bodyHtml: '',
};
this.forceUpdate_ = false;
this.isMounted_ = false;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
this.reloadNote = this.reloadNote.bind(this);
this.watchFn = this.watchFn.bind(this);
}
componentDidMount() {
this.isMounted_ = true;
}
componentWillUnmount() {
this.markupToHtml_ = null;
this.isMounted_ = false;
}
async reloadNote() {
this.forceUpdate_ = false;
const note = this.props.note;
const theme = themeStyle(this.props.themeId);
const bodyToRender = note ? note.body : '';
const mdOptions = {
onResourceLoaded: () => {
if (this.resourceLoadedTimeoutId_) {
shim.clearTimeout(this.resourceLoadedTimeoutId_);
this.resourceLoadedTimeoutId_ = null;
}
this.resourceLoadedTimeoutId_ = shim.setTimeout(() => {
this.resourceLoadedTimeoutId_ = null;
this.forceUpdate();
}, 100);
},
highlightedKeywords: this.props.highlightedKeywords,
resources: this.props.noteResources,
codeTheme: theme.codeThemeCss,
postMessageSyntax: 'window.joplinPostMessage_',
enableLongPress: shim.isReactNative(),
longPressDelay: 500, // TODO use system value
};
const result = await this.markupToHtml_.render(
note.markup_language,
bodyToRender,
{
bodyPaddingTop: '.8em', // Extra top padding on the rendered MD so it doesn't touch the border
bodyPaddingBottom: this.props.paddingBottom, // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
...this.props.webViewStyle,
},
mdOptions
);
let html = result.html;
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
const injectedJs = [];
injectedJs.push(shim.injectedJs('webviewLib'));
// Note that this postMessage function accepts two arguments, for compatibility with the desktop version, but
// the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app).
injectedJs.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
injectedJs.push(`
const readyStateCheckInterval = shim.setInterval(function() {
if (document.readyState === "complete") {
shim.clearInterval(readyStateCheckInterval);
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
const hash = "${this.props.noteHash}";
// Gives it a bit of time before scrolling to the anchor
// so that images are loaded.
if (hash) {
shim.setTimeout(() => {
const e = document.getElementById(hash);
if (!e) {
console.warn('Cannot find hash', hash);
return;
}
e.scrollIntoView();
}, 500);
}
}
}, 10);
`);
html =
`
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
${assetsToHeaders(result.pluginAssets, { asHtml: true })}
</head>
<body>
${html}
</body>
</html>
`;
// On iOS scalesPageToFit work like this:
//
// Find the widest image, resize it *and everything else* by x% so that
// the image fits within the viewport. The problem is that it means if there's
// a large image, everything is going to be scaled to a very small size, making
// the text unreadable.
//
// On Android:
//
// Find the widest elements and scale them (and them only) to fit within the viewport
// It means it's going to scale large images, but the text will remain at the normal
// size.
//
// That means we can use scalesPageToFix on Android but not on iOS.
// The weird thing is that on iOS, scalesPageToFix=false along with a CSS
// rule "img { max-width: 100% }", works like scalesPageToFix=true on Android.
// So we use scalesPageToFix=false on iOS along with that CSS rule.
// `baseUrl` is where the images will be loaded from. So images must use a path relative to resourceDir.
return {
source: {
html: html,
baseUrl: `file://${Setting.value('resourceDir')}/`,
},
injectedJs: injectedJs,
};
}
onLoadEnd() {
shim.setTimeout(() => {
if (this.props.onLoadEnd) this.props.onLoadEnd();
}, 100);
if (this.state.webViewLoaded) return;
// Need to display after a delay to avoid a white flash before
// the content is displayed.
shim.setTimeout(() => {
if (!this.isMounted_) return;
this.setState({ webViewLoaded: true });
}, 100);
}
shouldComponentUpdate(nextProps, nextState) {
const safeGetNoteProp = (props, propName) => {
if (!props) return null;
if (!props.note) return null;
return props.note[propName];
};
// To address https://github.com/laurent22/joplin/issues/433
// If a checkbox in a note is ticked, the body changes, which normally would trigger a re-render
// of this component, which has the unfortunate side effect of making the view scroll back to the top.
// This re-rendering however is uncessary since the component is already visually updated via JS.
// So here, if the note has not changed, we prevent the component from updating.
// This fixes the above issue. A drawback of this is if the note is updated via sync, this change
// will not be displayed immediately.
const currentNoteId = safeGetNoteProp(this.props, 'id');
const nextNoteId = safeGetNoteProp(nextProps, 'id');
if (currentNoteId !== nextNoteId || nextState.webViewLoaded !== this.state.webViewLoaded) return true;
// If the length of the body has changed, then it's something other than a checkbox that has changed,
// for example a resource that has been attached to the note while in View mode. In that case, update.
return (`${safeGetNoteProp(this.props, 'body')}`).length !== (`${safeGetNoteProp(nextProps, 'body')}`).length;
}
rebuildMd() {
this.forceUpdate_ = true;
this.forceUpdate();
}
watchFn() {
// react-async will not fetch the data again after the first render
// so we use this watchFn function to force it to reload in certain
// cases. It is used in particular when re-rendering the note when
// a resource has been downloaded in auto mode.
return this.forceUpdate_;
}
async onResourceLongPress(msg) {
try {
const resourceId = msg.split(':')[1];
const resource = await Resource.load(resourceId);
const name = resource.title ? resource.title : resource.file_name;
const action = await dialogs.pop(this, name, [
{ text: _('Open'), id: 'open' },
{ text: _('Share'), id: 'share' },
]);
if (action === 'open') {
this.props.onJoplinLinkClick(`joplin://${resourceId}`);
} else if (action === 'share') {
const filename = resource.file_name ?
`${resource.file_name}.${resource.file_extension}` :
resource.title;
const targetPath = `${Setting.value('resourceDir')}/${filename}`;
await shim.fsDriver().copy(Resource.fullPath(resource), targetPath);
await Share.open({
type: resource.mime,
filename: resource.title,
url: `file://${targetPath}`,
failOnCancel: false,
});
await shim.fsDriver().remove(targetPath);
}
} catch (e) {
reg.logger().error('Could not handle link long press', e);
ToastAndroid.show('An error occurred, check log for details', ToastAndroid.SHORT);
}
}
render() {
// Note: useWebKit={false} is needed to go around this bug:
// https://github.com/react-native-community/react-native-webview/issues/376
// However, if we add the <meta> tag as described there, it is no longer necessary and WebKit can be used!
// https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-501991406
//
// However, on iOS, due to the bug below, we cannot use WebKit:
// https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-503754654
const webViewStyle = { backgroundColor: this.props.webViewStyle.backgroundColor };
// On iOS, the onLoadEnd() event is never fired so always
// display the webview (don't do the little trick
// to avoid the white flash).
if (Platform.OS !== 'ios') {
webViewStyle.opacity = this.state.webViewLoaded ? 1 : 0.01;
}
return (
<View style={this.props.style}>
<Async promiseFn={this.reloadNote} watchFn={this.watchFn}>
{({ data, error, isPending }) => {
if (error) {
console.error(error);
return <Text>{error.message}</Text>;
}
if (isPending) return null;
return (
<WebView
useWebKit={Platform.OS !== 'ios'}
style={webViewStyle}
source={data.source}
injectedJavaScript={data.injectedJs.join('\n')}
originWhitelist={['file://*', './*', 'http://*', 'https://*']}
mixedContentMode="always"
allowFileAccess={true}
onLoadEnd={() => this.onLoadEnd()}
onError={() => reg.logger().error('WebView error')}
onMessage={event => {
// Since RN 58 (or 59) messages are now escaped twice???
let msg = unescape(unescape(event.nativeEvent.data));
console.info('Got IPC message: ', msg);
if (msg.indexOf('checkboxclick:') === 0) {
const newBody = shared.toggleCheckbox(msg, this.props.note.body);
if (this.props.onCheckboxChange) this.props.onCheckboxChange(newBody);
} else if (msg.indexOf('markForDownload:') === 0) {
msg = msg.split(':');
const resourceId = msg[1];
if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId });
} else if (msg.startsWith('longclick:')) {
this.onResourceLongPress(msg);
} else if (msg.startsWith('joplin:')) {
this.props.onJoplinLinkClick(msg);
}
}}
/>
);
}}
</Async>
<BackButtonDialogBox
ref={dialogbox => {
this.dialogbox = dialogbox;
}}
/>
</View>
);
}
}
module.exports = { NoteBodyViewer };

View File

@ -264,7 +264,7 @@ class ScreenHeaderComponent extends React.PureComponent {
const renderUndoButton = () => {
return renderTopButton({
iconName: 'md-undo',
iconName: 'arrow-undo-circle-sharp',
onPress: this.props.onUndoButtonPress,
visible: this.props.showUndoButton,
disabled: this.props.undoButtonDisabled,
@ -273,7 +273,7 @@ class ScreenHeaderComponent extends React.PureComponent {
const renderRedoButton = () => {
return renderTopButton({
iconName: 'md-redo',
iconName: 'arrow-redo-circle-sharp',
onPress: this.props.onRedoButtonPress,
visible: this.props.showRedoButton,
});
@ -323,7 +323,7 @@ class ScreenHeaderComponent extends React.PureComponent {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.iconButton}>
<Icon name="md-funnel" style={styles.topIcon} />
<Icon name="filter-outline" style={styles.topIcon} />
</View>
</TouchableOpacity>
);
@ -481,7 +481,7 @@ class ScreenHeaderComponent extends React.PureComponent {
!menuOptionComponents.length || !showContextMenuButton ? null : (
<Menu onSelect={value => this.menu_select(value)} style={this.styles().contextMenu}>
<MenuTrigger style={contextMenuStyle}>
<Icon name="md-more" style={this.styles().contextMenuTrigger} />
<Icon name="md-ellipsis-vertical" style={this.styles().contextMenuTrigger} />
</MenuTrigger>
<MenuOptions>
<ScrollView style={{ maxHeight: windowHeight }}>{menuOptionComponents}</ScrollView>

View File

@ -1,18 +1,22 @@
import FileViewer from 'react-native-file-viewer';
import AsyncActionQueue from '../../AsyncActionQueue';
import UndoRedoService from 'lib/services/UndoRedoService';
import uuid from 'lib/uuid';
import Setting from 'lib/models/Setting';
import shim from 'lib/shim';
import NoteBodyViewer from 'lib/components/NoteBodyViewer/NoteBodyViewer';
import checkPermissions from 'lib/checkPermissions';
const FileViewer = require('react-native-file-viewer').default;
const React = require('react');
const { Platform, Clipboard, Keyboard, View, TextInput, StyleSheet, Linking, Image, Share } = require('react-native');
const { Platform, Keyboard, View, TextInput, StyleSheet, Linking, Image, Share, PermissionsAndroid } = require('react-native');
const { connect } = require('react-redux');
const uuid = require('lib/uuid').default;
const { MarkdownEditor } = require('../../../MarkdownEditor/index.js');
const RNFS = require('react-native-fs');
const Note = require('lib/models/Note.js');
const UndoRedoService = require('lib/services/UndoRedoService.js').default;
const BaseItem = require('lib/models/BaseItem.js');
const Setting = require('lib/models/Setting').default;
const Resource = require('lib/models/Resource.js');
const Folder = require('lib/models/Folder.js');
const Clipboard = require('@react-native-community/clipboard').default;
const md5 = require('md5');
const { BackButtonService } = require('lib/services/back-button.js');
const NavService = require('lib/services/NavService.js');
@ -26,24 +30,24 @@ const { time } = require('lib/time-utils.js');
const { Checkbox } = require('lib/components/checkbox.js');
const { _ } = require('lib/locale');
const { reg } = require('lib/registry.js');
const shim = require('lib/shim').default;
const ResourceFetcher = require('lib/services/ResourceFetcher');
const { BaseScreenComponent } = require('lib/components/base-screen.js');
const { themeStyle, editorFont } = require('lib/components/global-style.js');
const { dialogs } = require('lib/dialogs.js');
const DialogBox = require('react-native-dialogbox').default;
const { NoteBodyViewer } = require('lib/components/note-body-viewer.js');
const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker');
const DocumentPicker = require('react-native-document-picker').default;
const ImageResizer = require('react-native-image-resizer').default;
const shared = require('lib/components/shared/note-screen-shared.js');
const ImagePicker = require('react-native-image-picker');
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
const ImagePicker = require('react-native-image-picker').default;
const SelectDateTimeDialog = require('lib/components/SelectDateTimeDialog').default;
const ShareExtension = require('lib/ShareExtension.js').default;
const CameraView = require('lib/components/CameraView');
const CameraView = require('lib/components/CameraView').default;
const urlUtils = require('lib/urlUtils');
const emptyArray:any[] = [];
class NoteScreenComponent extends BaseScreenComponent {
static navigationOptions() {
static navigationOptions():any {
return { header: null };
}
@ -151,7 +155,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ noteTagDialogShown: false });
};
this.onJoplinLinkClick_ = async msg => {
this.onJoplinLinkClick_ = async (msg:string) => {
try {
if (msg.indexOf('joplin://') === 0) {
const resourceUrlInfo = urlUtils.parseResourceUrl(msg);
@ -194,7 +198,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
};
this.refreshResource = async (resource, noteBody = null) => {
this.refreshResource = async (resource:any, noteBody:string = null) => {
if (noteBody === null && this.state.note && this.state.note.body) noteBody = this.state.note.body;
if (noteBody === null) return;
@ -202,9 +206,7 @@ class NoteScreenComponent extends BaseScreenComponent {
if (resourceIds.indexOf(resource.id) >= 0) {
shared.clearResourceCache();
const attachedResources = await shared.attachedResources(noteBody);
this.setState({ noteResources: attachedResources }, () => {
if (this.refs.noteBodyViewer) this.refs.noteBodyViewer.rebuildMd();
});
this.setState({ noteResources: attachedResources });
}
};
@ -230,6 +232,8 @@ class NoteScreenComponent extends BaseScreenComponent {
this.screenHeader_undoButtonPress = this.screenHeader_undoButtonPress.bind(this);
this.screenHeader_redoButtonPress = this.screenHeader_redoButtonPress.bind(this);
this.body_selectionChange = this.body_selectionChange.bind(this);
this.onBodyViewerLoadEnd = this.onBodyViewerLoadEnd.bind(this);
this.onBodyViewerCheckboxChange = this.onBodyViewerCheckboxChange.bind(this);
}
undoRedoService_stackChange() {
@ -239,11 +243,11 @@ class NoteScreenComponent extends BaseScreenComponent {
} });
}
async undoRedo(type) {
async undoRedo(type:string) {
const undoState = await this.undoRedoService_[type](this.undoState());
if (!undoState) return;
this.setState((state) => {
this.setState((state:any) => {
const newNote = Object.assign({}, state.note);
newNote.body = undoState.body;
return {
@ -270,7 +274,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.styles_ = {};
// TODO: Clean up these style names and nesting
const styles = {
const styles:any = {
screen: {
flex: 1,
backgroundColor: theme.backgroundColor,
@ -299,12 +303,6 @@ class NoteScreenComponent extends BaseScreenComponent {
paddingLeft: theme.marginLeft,
paddingRight: theme.marginRight,
},
noteBodyViewerPreview: {
borderTopColor: theme.dividerColor,
borderTopWidth: 1,
borderBottomColor: theme.dividerColor,
borderBottomWidth: 1,
},
checkbox: {
color: theme.color,
paddingRight: 10,
@ -318,6 +316,14 @@ class NoteScreenComponent extends BaseScreenComponent {
},
};
styles.noteBodyViewerPreview = {
...styles.noteBodyViewer,
borderTopColor: theme.dividerColor,
borderTopWidth: 1,
borderBottomColor: theme.dividerColor,
borderBottomWidth: 1,
};
styles.titleContainer = {
flex: 0,
flexDirection: 'row',
@ -353,12 +359,29 @@ class NoteScreenComponent extends BaseScreenComponent {
return shared.isModified(this);
}
undoState(noteBody = null) {
undoState(noteBody:string = null) {
return {
body: noteBody === null ? this.state.note.body : noteBody,
};
}
async requestGeoLocationPermissions() {
if (!Setting.value('trackLocation')) return;
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, {
message: _('In order to associate a geo-location with the note, the app needs your permission to access your location.\n\nYou may turn off this option at any time in the Configuration screen.'),
title: _('Permission needed'),
});
// If the user simply pressed "Deny", we don't automatically switch it off because they might accept
// once we show the rationale again on second try. If they press "Never again" however we switch it off.
// https://github.com/zoontek/react-native-permissions/issues/385#issuecomment-563132396
if (response === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
reg.logger().info('Geo-location tracking has been automatically disabled');
Setting.setValue('trackLocation', false);
}
}
async componentDidMount() {
BackButtonService.addHandler(this.backHandler);
NavService.addHandler(this.navHandler);
@ -375,13 +398,18 @@ class NoteScreenComponent extends BaseScreenComponent {
const resourceIds = await Note.linkedResourceIds(this.state.note.body);
await ResourceFetcher.instance().markForDownload(resourceIds);
}
// Although it is async, we don't wait for the answer so that if permission
// has already been granted, it doesn't slow down opening the note. If it hasn't
// been granted, the popup will open anyway.
this.requestGeoLocationPermissions();
}
onMarkForDownload(event) {
onMarkForDownload(event:any) {
ResourceFetcher.instance().markForDownload(event.resourceId);
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps:any) {
if (this.doFocusUpdate_) {
this.doFocusUpdate_ = false;
this.focusUpdate();
@ -412,13 +440,13 @@ class NoteScreenComponent extends BaseScreenComponent {
if (this.undoRedoService_) this.undoRedoService_.off('stackChange', this.undoRedoService_stackChange);
}
title_changeText(text) {
title_changeText(text:string) {
shared.noteComponent_change(this, 'title', text);
this.setState({ newAndNoTitleChangeNoteId: null });
this.scheduleSave();
}
body_changeText(text) {
body_changeText(text:string) {
if (!this.undoRedoService_.canUndo) {
this.undoRedoService_.push(this.undoState());
} else {
@ -428,7 +456,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.scheduleSave();
}
body_selectionChange(event) {
body_selectionChange(event:any) {
this.selection = event.nativeEvent.selection;
}
@ -438,7 +466,7 @@ class NoteScreenComponent extends BaseScreenComponent {
};
}
saveActionQueue(noteId) {
saveActionQueue(noteId:string) {
if (!this.saveActionQueues_[noteId]) {
this.saveActionQueues_[noteId] = new AsyncActionQueue(500);
}
@ -449,13 +477,13 @@ class NoteScreenComponent extends BaseScreenComponent {
this.saveActionQueue(this.state.note.id).push(this.makeSaveAction());
}
async saveNoteButton_press(folderId = null) {
async saveNoteButton_press(folderId:string = null) {
await shared.saveNoteButton_press(this, folderId);
Keyboard.dismiss();
}
async saveOneProperty(name, value) {
async saveOneProperty(name:string, value:any) {
await shared.saveOneProperty(this, name, value);
}
@ -478,47 +506,45 @@ class NoteScreenComponent extends BaseScreenComponent {
}
async pickDocument() {
return new Promise((resolve) => {
DocumentPicker.show({ filetype: [DocumentPickerUtil.allFiles()] }, (error, res) => {
if (error) {
// Also returns an error if the user doesn't pick a file
// so just resolve with null.
console.info('pickDocument error:', error);
resolve(null);
return;
}
resolve(res);
});
});
try {
const result = await DocumentPicker.pick();
return result;
} catch (error) {
if (DocumentPicker.isCancel(error)) {
console.info('pickDocument: user has cancelled');
return null;
} else {
throw error;
}
}
}
async imageDimensions(uri) {
async imageDimensions(uri:string) {
return new Promise((resolve, reject) => {
Image.getSize(
uri,
(width, height) => {
(width:number, height:number) => {
resolve({ width: width, height: height });
},
error => {
(error:any) => {
reject(error);
}
);
});
}
showImagePicker(options) {
showImagePicker(options:any) {
return new Promise((resolve) => {
ImagePicker.launchImageLibrary(options, response => {
ImagePicker.launchImageLibrary(options, (response:any) => {
resolve(response);
});
});
}
async resizeImage(localFilePath, targetPath, mimeType) {
async resizeImage(localFilePath:string, targetPath:string, mimeType:string) {
const maxSize = Resource.IMAGE_MAX_DIMENSION;
const dimensions = await this.imageDimensions(localFilePath);
const dimensions:any = await this.imageDimensions(localFilePath);
reg.logger().info('Original dimensions ', dimensions);
@ -564,9 +590,9 @@ class NoteScreenComponent extends BaseScreenComponent {
return true;
}
async attachFile(pickerResponse, fileType) {
async attachFile(pickerResponse:any, fileType:string) {
if (!pickerResponse) {
reg.logger().warn('Got no response from picker');
// User has cancelled
return;
}
@ -674,7 +700,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ showCamera: true });
}
cameraView_onPhoto(data) {
cameraView_onPhoto(data:any) {
this.attachFile(
{
uri: data.uri,
@ -724,7 +750,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ alarmDialogShown: true });
}
async onAlarmDialogAccept(date) {
async onAlarmDialogAccept(date:Date) {
const newNote = Object.assign({}, this.state.note);
newNote.todo_due = date ? date.getTime() : 0;
@ -811,17 +837,35 @@ class NoteScreenComponent extends BaseScreenComponent {
// The file attachement modules only work in Android >= 5 (Version 21)
// https://github.com/react-community/react-native-image-picker/issues/606
// As of 2020-10-13, support for attaching images from the gallery is removed
// as the package react-native-image-picker has permission issues. It's still
// possible to attach files, which has often a similar UI, with thumbnails for
// images so normally it should be enough.
let canAttachPicture = true;
if (Platform.OS === 'android' && Platform.Version < 21) canAttachPicture = false;
if (canAttachPicture) {
output.push({
title: _('Attach...'),
onPress: async () => {
const buttonId = await dialogs.pop(this, _('Choose an option'), [{ text: _('Take photo'), id: 'takePhoto' }, { text: _('Attach photo'), id: 'attachPhoto' }, { text: _('Attach any file'), id: 'attachFile' }]);
const buttons = [];
// On iOS, it will show "local files", which means certain files saved from the browser
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
//
// On Android, it will depend on the phone, but usually it will allow browing all files and photos.
buttons.push({ text: _('Attach file'), id: 'attachFile' });
// Disabled on Android because it doesn't work due to permission issues, but enabled on iOS
// because that's only way to browse photos from the camera roll.
if (Platform.OS === 'ios') buttons.push({ text: _('Attach photo'), id: 'attachPhoto' });
buttons.push({ text: _('Take photo'), id: 'takePhoto' });
const buttonId = await dialogs.pop(this, _('Choose an option'), buttons);
if (buttonId === 'takePhoto') this.takePhoto_onPress();
if (buttonId === 'attachPhoto') this.attachPhoto_onPress();
if (buttonId === 'attachFile') this.attachFile_onPress();
if (buttonId === 'attachPhoto') this.attachPhoto_onPress();
},
});
}
@ -882,11 +926,11 @@ class NoteScreenComponent extends BaseScreenComponent {
return output;
}
async todoCheckbox_change(checked) {
async todoCheckbox_change(checked:boolean) {
await this.saveOneProperty('todo_completed', checked ? time.unixMs() : 0);
}
titleTextInput_contentSizeChange(event) {
titleTextInput_contentSizeChange(event:any) {
if (!this.enableMultilineTitle_) return;
const height = event.nativeEvent.contentSize.height;
@ -920,7 +964,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
}
async folderPickerOptions_valueChanged(itemValue) {
async folderPickerOptions_valueChanged(itemValue:any) {
const note = this.state.note;
const isProvisionalNote = this.props.provisionalNoteIds.includes(note.id);
@ -954,6 +998,19 @@ class NoteScreenComponent extends BaseScreenComponent {
return this.folderPickerOptions_;
}
onBodyViewerLoadEnd() {
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 1 });
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 0 });
}, 50);
}, 5);
}
onBodyViewerCheckboxChange(newBody:string) {
this.saveOneProperty('body', newBody);
}
render() {
if (this.state.isLoading) {
return (
@ -971,65 +1028,39 @@ class NoteScreenComponent extends BaseScreenComponent {
return <CameraView themeId={this.props.themeId} style={{ flex: 1 }} onPhoto={this.cameraView_onPhoto} onCancel={this.cameraView_onCancel} />;
}
// Currently keyword highlighting is supported only when FTS is available.
const keywords = this.props.searchQuery && !!this.props.ftsEnabled ? this.props.highlightedWords : emptyArray;
let bodyComponent = null;
if (this.state.mode == 'view' && !this.useBetaEditor()) {
const onCheckboxChange = newBody => {
this.saveOneProperty('body', newBody);
};
// Currently keyword highlighting is supported only when FTS is available.
let keywords = [];
if (this.props.searchQuery && !!this.props.ftsEnabled) {
keywords = this.props.highlightedWords;
}
// Note: as of 2018-12-29 it's important not to display the viewer if the note body is empty,
// to avoid the HACK_webviewLoadingState related bug.
bodyComponent =
!note || !note.body.trim() ? null : (
<NoteBodyViewer
onJoplinLinkClick={this.onJoplinLinkClick_}
ref="noteBodyViewer"
style={this.styles().noteBodyViewer}
webViewStyle={theme}
// Extra bottom padding to make it possible to scroll past the
// action button (so that it doesn't overlap the text)
paddingBottom="150"
note={note}
paddingBottom={150}
noteBody={note.body}
noteMarkupLanguage={note.markup_language}
noteResources={this.state.noteResources}
highlightedKeywords={keywords}
themeId={this.props.themeId}
noteHash={this.props.noteHash}
onCheckboxChange={newBody => {
onCheckboxChange(newBody);
}}
onCheckboxChange={this.onBodyViewerCheckboxChange}
onMarkForDownload={this.onMarkForDownload}
onLoadEnd={() => {
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 1 });
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 0 });
}, 50);
}, 5);
}}
onLoadEnd={this.onBodyViewerLoadEnd}
/>
);
} else {
// autoFocus={fieldToFocus === 'body'}
// Currently keyword highlighting is supported only when FTS is available.
let keywords = [];
if (this.props.searchQuery && !!this.props.ftsEnabled) {
keywords = this.props.highlightedWords;
}
const onCheckboxChange = newBody => {
this.saveOneProperty('body', newBody);
};
bodyComponent = this.useBetaEditor()
// Note: blurOnSubmit is necessary to get multiline to work.
// See https://github.com/facebook/react-native/issues/12717#issuecomment-327001997
//
// 2020-10-16: As of React Native 0.63, the Markdown Editor no longer crashes in Android, however the
// cursor is still too unreliable to be usable, so we disable it in Android.
? <MarkdownEditor
ref={this.markdownEditorRef} // For focusing the Markdown editor
editorFont={editorFont(this.props.editorFont)}
@ -1038,7 +1069,7 @@ class NoteScreenComponent extends BaseScreenComponent {
value={note.body}
borderColor={this.styles().markdownButtons.borderColor}
markdownButtonsColor={this.styles().markdownButtons.color}
saveText={text => this.body_changeText(text)}
saveText={(text:string) => this.body_changeText(text)}
blurOnSubmit={false}
selectionColor={theme.textSelectionColor}
keyboardAppearance={theme.keyboardAppearance}
@ -1046,29 +1077,18 @@ class NoteScreenComponent extends BaseScreenComponent {
placeholderTextColor={theme.colorFaded}
noteBodyViewer={{
onJoplinLinkClick: this.onJoplinLinkClick_,
ref: 'noteBodyViewer',
style: {
...this.styles().noteBodyViewer,
...this.styles().noteBodyViewerPreview,
},
style: this.styles().noteBodyViewerPreview,
paddingBottom: 0,
webViewStyle: theme,
note: note,
noteBody: note.body,
noteMarkupLanguage: note.markup_language,
noteResources: this.state.noteResources,
highlightedKeywords: keywords,
themeId: this.props.themeId,
noteHash: this.props.noteHash,
onCheckboxChange: newBody => {
onCheckboxChange(newBody);
},
onCheckboxChange: this.onBodyViewerCheckboxChange,
onMarkForDownload: this.onMarkForDownload,
onLoadEnd: () => {
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 1 });
shim.setTimeout(() => {
this.setState({ HACK_webviewLoadingState: 0 });
}, 50);
}, 5);
},
onLoadEnd: this.onBodyViewerLoadEnd,
}}
/>
@ -1095,7 +1115,7 @@ class NoteScreenComponent extends BaseScreenComponent {
ref="noteBodyTextField"
multiline={true}
value={note.body}
onChangeText={(text) => this.body_changeText(text)}
onChangeText={(text:string) => this.body_changeText(text)}
onSelectionChange={this.body_selectionChange}
blurOnSubmit={false}
selectionColor={theme.textSelectionColor}
@ -1178,10 +1198,10 @@ class NoteScreenComponent extends BaseScreenComponent {
{bodyComponent}
{!this.useBetaEditor() && actionButtonComp}
<SelectDateTimeDialog shown={this.state.alarmDialogShown} date={dueDate} onAccept={this.onAlarmDialogAccept} onReject={this.onAlarmDialogReject} />
<SelectDateTimeDialog themeId={this.props.themeId} shown={this.state.alarmDialogShown} date={dueDate} onAccept={this.onAlarmDialogAccept} onReject={this.onAlarmDialogReject} />
<DialogBox
ref={dialogbox => {
ref={(dialogbox:any) => {
this.dialogbox = dialogbox;
}}
/>
@ -1191,7 +1211,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
}
const NoteScreen = connect(state => {
const NoteScreen = connect((state:any) => {
return {
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
noteHash: state.selectedNoteHash,
@ -1209,4 +1229,4 @@ const NoteScreen = connect(state => {
};
})(NoteScreenComponent);
module.exports = { NoteScreen };
export default NoteScreen;

View File

@ -100,8 +100,8 @@ class ConfigScreenComponent extends BaseScreenComponent {
const exportPath = this.state.profileExportPath;
const resourcePath = `${exportPath}/resources`;
try {
const hasPermissions = await checkPermissions(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (!hasPermissions) {
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (response !== PermissionsAndroid.RESULTS.GRANTED) {
throw new Error('Permission denied');
}

View File

@ -1,109 +0,0 @@
import React from 'react';
import { View } from 'react-native';
import PopupDialog, { DialogTitle, DialogButton } from 'react-native-popup-dialog';
import DatePicker from 'react-native-datepicker';
import moment from 'moment';
import { _ } from 'lib/locale.js';
const { time } = require('lib/time-utils.js');
class SelectDateTimeDialog extends React.PureComponent {
constructor() {
super();
this.dialog_ = null;
this.shown_ = false;
this.state = { date: null };
this.onReject = this.onReject.bind(this);
}
UNSAFE_componentWillReceiveProps(newProps) {
if (newProps.date != this.state.date) {
this.setState({ date: newProps.date });
}
if ('shown' in newProps && newProps.shown != this.shown_) {
this.show(newProps.shown);
}
}
show(doShow = true) {
if (doShow) {
this.dialog_.show();
} else {
this.dialog_.dismiss();
}
this.shown_ = doShow;
}
dismiss() {
this.show(false);
}
dateTimeFormat() {
return time.dateTimeFormat();
}
stringToDate(s) {
return moment(s, this.dateTimeFormat()).toDate();
}
onAccept() {
if (this.props.onAccept) this.props.onAccept(this.state.date);
}
onReject() {
if (this.props.onReject) this.props.onReject();
}
onClear() {
if (this.props.onAccept) this.props.onAccept(null);
}
render() {
const clearAlarmText = _('Clear alarm'); // For unknown reasons, this particular string doesn't get translated if it's directly in the text property below
const popupActions = [
<DialogButton text={_('Save alarm')} align="center" onPress={() => this.onAccept()} key="saveButton" />,
<DialogButton text={clearAlarmText} align="center" onPress={() => this.onClear()} key="clearButton" />,
<DialogButton text={_('Cancel')} align="center" onPress={() => this.onReject()} key="cancelButton" />,
];
return (
<PopupDialog
ref={(dialog) => { this.dialog_ = dialog; }}
dialogTitle={<DialogTitle title={_('Set alarm')} />}
actions={popupActions}
dismissOnTouchOutside={false}
width={0.9}
height={350}
>
<View style={{ flex: 1, margin: 20, alignItems: 'center' }}>
<DatePicker
date={this.state.date}
mode="datetime"
placeholder={_('Select date')}
format={this.dateTimeFormat()}
confirmBtnText={_('Confirm')}
cancelBtnText={_('Cancel')}
onDateChange={(date) => { this.setState({ date: this.stringToDate(date) }); }}
style={{ width: 300 }}
customStyles={{
btnConfirm: {
paddingVertical: 0,
},
btnCancel: {
paddingVertical: 0,
},
}}
/>
</View>
</PopupDialog>
);
}
}
// eslint-disable-next-line import/prefer-default-export
export { SelectDateTimeDialog };

View File

@ -237,7 +237,7 @@ class SideMenuContentComponent extends Component {
let iconWrapper = null;
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'md-arrow-dropdown' : 'md-arrow-dropup';
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'md-arrow-down' : 'md-arrow-up';
const iconComp = <Icon name={iconName} style={this.styles().folderIcon} />;
iconWrapper = !hasChildren ? null : (

View File

@ -1,6 +1,10 @@
// Use this to show which props have been changed within a component.
//
// Usage: usePropsDebugger(props);
import useEffectDebugger from './useEffectDebugger';
export default function usePropsDebugger(effectHook:any, props:any) {
export default function usePropsDebugger(props:any) {
const dependencies:any[] = [];
const dependencyNames:string[] = [];
@ -9,5 +13,5 @@ export default function usePropsDebugger(effectHook:any, props:any) {
dependencyNames.push(k);
}
useEffectDebugger(effectHook, dependencies, dependencyNames);
useEffectDebugger(() => {}, dependencies, dependencyNames);
}

View File

@ -3,15 +3,20 @@ const utils = require('./utils');
const noteStyle = require('./noteStyle');
const Setting = require('lib/models/Setting').default;
const { themeStyle } = require('lib/theme');
const memoryCache = require('memory-cache');
const InMemoryCache = require('lib/InMemoryCache').default;
const md5 = require('md5');
// Renderered notes can potentially be quite large (for example
// when they come from the clipper) so keep the cache size
// relatively small.
const inMemoryCache = new InMemoryCache(10);
class HtmlToHtml {
constructor(options) {
if (!options) options = {};
this.resourceBaseUrl_ = 'resourceBaseUrl' in options ? options.resourceBaseUrl : null;
this.ResourceModel_ = options.ResourceModel;
this.cache_ = new memoryCache.Cache();
this.cache_ = inMemoryCache;
this.fsDriver_ = {
writeFile: (/* path, content, encoding = 'base64'*/) => { throw new Error('writeFile not set'); },
exists: (/* path*/) => { throw new Error('exists not set'); },
@ -55,7 +60,7 @@ class HtmlToHtml {
}, options);
const cacheKey = md5(escape(markup));
let html = this.cache_.get(cacheKey);
let html = this.cache_.value(cacheKey);
if (!html) {
html = htmlUtils.sanitizeHtml(markup);
@ -80,7 +85,7 @@ class HtmlToHtml {
});
}
this.cache_.put(cacheKey, html, 1000 * 60 * 10);
this.cache_.setValue(cacheKey, html, 1000 * 60 * 10);
if (options.bodyOnly) {
return {

View File

@ -63,6 +63,11 @@ class MarkupToHtml {
return output;
}
clearCache(markupLanguage) {
const r = this.renderer(markupLanguage);
if (r.clearCache) r.clearCache();
}
async render(markupLanguage, markup, theme, options) {
return this.renderer(markupLanguage).render(markup, theme, options);
}

View File

@ -2,7 +2,7 @@ const MarkdownIt = require('markdown-it');
const md5 = require('md5');
const noteStyle = require('./noteStyle');
const { fileExtension } = require('./pathUtils');
const memoryCache = require('memory-cache');
const InMemoryCache = require('lib/InMemoryCache').default;
// /!\/!\ Note: the order of rules is important!! /!\/!\
const rules = {
@ -44,6 +44,9 @@ function slugify(s) {
return nodeSlug(s);
}
// Share across all instances of MdToHtml
const inMemoryCache = new InMemoryCache(20);
class MdToHtml {
constructor(options = null) {
if (!options) options = {};
@ -57,7 +60,7 @@ class MdToHtml {
this.cachedHighlightedCode_ = {};
this.ResourceModel_ = options.ResourceModel;
this.pluginOptions_ = options.pluginOptions ? options.pluginOptions : {};
this.contextCache_ = new memoryCache.Cache();
this.contextCache_ = inMemoryCache;
this.tempDir_ = options.tempDir;
this.fsDriver_ = {
@ -175,7 +178,11 @@ class MdToHtml {
return html.substring(3, html.length - 5);
}
// "style" here is really the theme, as returned by themeStyle()
clearCache() {
this.cachedOutputs_ = {};
}
// "theme" is the theme as returned by themeStyle()
async render(body, theme = null, options = null) {
options = Object.assign({}, {
// In bodyOnly mode, the rendered Markdown is returned without the wrapper DIV

View File

@ -16,7 +16,7 @@ function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any
}
const cacheKey = md5(escape(token.content));
let sanitizedContent = context.cache.get(cacheKey);
let sanitizedContent = context.cache.value(cacheKey);
// For html_inline, the content is only a fragment of HTML, as it will be rendered, but
// it's not necessarily valid HTML. For example this HTML:
@ -37,7 +37,7 @@ function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any
token.content = sanitizedContent;
context.cache.put(cacheKey, sanitizedContent, 1000 * 60 * 60);
context.cache.setValue(cacheKey, sanitizedContent, 1000 * 60 * 60);
walkHtmlTokens(token.children);
}
};

View File

@ -855,11 +855,6 @@
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
},
"memory-cache": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo="
},
"mermaid": {
"version": "8.8.1",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.8.1.tgz",
@ -972,9 +967,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"slug": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/slug/-/slug-3.3.5.tgz",
"integrity": "sha512-d/9yTbJDtSIhJThaNRP/U5uxwCl0mWIlV42JmKSfvg8t7DiVt69G8rAWTc0FWhaQOier0fiNAWVs7ctvVhK1RA=="
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/slug/-/slug-3.5.1.tgz",
"integrity": "sha512-ei0JnJzg8HKhLunZy+vpNlILRRradfaAQ+p2YEI4b4r8yX/5TlFi1JSwcYQCg7INZxdTC43BT68rHMkRxzn7Xg=="
},
"source-map": {
"version": "0.5.7",

View File

@ -36,7 +36,6 @@
"markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"memory-cache": "^0.2.0",
"mermaid": "^8.8.1",
"slug": "^3.5.0"
}

View File

@ -2,7 +2,7 @@ const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
export interface Notification {
id: string,
id: number,
noteId: string,
date: Date,
title: string,

View File

@ -100,6 +100,9 @@ class OneDriveApi {
const r = await shim.fetch(this.tokenBaseUrl(), {
method: 'POST',
body: body,
headers: {
['Content-Type']: 'application/x-www-form-urlencoded',
},
});
if (!r.ok) {

View File

@ -1,71 +0,0 @@
const { time } = require('lib/time-utils.js');
class PoorManIntervals {
static setInterval(callback, interval) {
PoorManIntervals.intervalId_++;
PoorManIntervals.intervals_.push({
id: PoorManIntervals.intervalId_,
callback: callback,
interval: interval,
lastIntervalTime: time.unixMs(),
});
return PoorManIntervals.intervalId_;
}
static setTimeout(callback, interval) {
PoorManIntervals.intervalId_++;
PoorManIntervals.intervals_.push({
id: PoorManIntervals.intervalId_,
callback: callback,
interval: interval,
lastIntervalTime: time.unixMs(),
oneOff: true,
});
return PoorManIntervals.intervalId_;
}
static intervalById(id) {
for (let i = 0; i < PoorManIntervals.intervals_.length; i++) {
if (PoorManIntervals.intervals_[i].id == id) return PoorManIntervals.intervals_[id];
}
return null;
}
static clearInterval(id) {
for (let i = 0; i < PoorManIntervals.intervals_.length; i++) {
if (PoorManIntervals.intervals_[i].id == id) {
PoorManIntervals.intervals_.splice(i, 1);
break;
}
}
}
static update() {
// Don't update more than once a second
if (PoorManIntervals.lastUpdateTime_ + 1000 > time.unixMs()) return;
for (let i = 0; i < PoorManIntervals.intervals_.length; i++) {
const interval = PoorManIntervals.intervals_[i];
const now = time.unixMs();
if (now - interval.lastIntervalTime >= interval.interval) {
interval.lastIntervalTime = now;
interval.callback();
if (interval.oneOff) {
this.clearInterval(interval.id);
}
}
}
PoorManIntervals.lastUpdateTime_ = time.unixMs();
}
}
PoorManIntervals.lastUpdateTime_ = 0;
PoorManIntervals.intervalId_ = 0;
PoorManIntervals.intervals_ = [];
module.exports = { PoorManIntervals };

View File

@ -1,48 +1,67 @@
import Logger from 'lib/Logger';
import { Notification } from 'lib/models/Alarm';
const PushNotification = require('react-native-push-notification');
const ReactNativeAN = require('react-native-alarm-notification').default;
export default class AlarmServiceDriver {
private PushNotification_:any = null;
private logger_:Logger;
PushNotificationHandler_() {
if (!this.PushNotification_) {
PushNotification.configure({
// (required) Called when a remote or local notification is opened or received
onNotification: function(notification:any) {
console.info('Notification was opened: ', notification);
// process the notification
},
popInitialNotification: true,
requestPermissions: true,
});
this.PushNotification_ = PushNotification;
}
return this.PushNotification_;
constructor(logger:Logger) {
this.logger_ = logger;
}
hasPersistentNotifications() {
public hasPersistentNotifications() {
return true;
}
notificationIsSet() {
public notificationIsSet() {
throw new Error('Available only for non-persistent alarms');
}
async clearNotification(id:any) {
return this.PushNotificationHandler_().cancelLocalNotifications({ id: `${id}` });
public async clearNotification(id:number) {
const alarm = await this.alarmByJoplinNotificationId(id);
if (!alarm) return;
this.logger_.info('AlarmServiceDriver: Deleting alarm:', alarm);
await ReactNativeAN.deleteAlarm(alarm.id);
}
async scheduleNotification(notification:Notification) {
const config = {
id: `${notification.id}`,
message: notification.title,
date: notification.date,
// Returns -1 if could not be found
private alarmJoplinAlarmId(alarm:any):number {
if (!alarm.data) return -1;
const m = alarm.data.match(/joplinNotificationId==>(\d+)/);
return m ? Number(m[1]) : -1;
}
private async alarmByJoplinNotificationId(joplinNotificationId:number) {
const alarms:any[] = await ReactNativeAN.getScheduledAlarms();
for (const alarm of alarms) {
const id = this.alarmJoplinAlarmId(alarm);
if (id === joplinNotificationId) return alarm;
}
this.logger_.warn('AlarmServiceDriver: Could not find alarm that matches Joplin notification ID. It could be because it has already been triggered:', joplinNotificationId);
return null;
}
public async scheduleNotification(notification:Notification) {
const alarmNotifData = {
title: notification.title,
message: notification.body ? notification.body : '-', // Required
channel: 'net.cozic.joplin.notification',
small_icon: 'ic_launcher',
color: 'white',
data: { joplinNotificationId: `${notification.id}` },
};
this.PushNotificationHandler_().localNotificationSchedule(config);
// ReactNativeAN expects a string as a date and it seems this utility
// function converts it to the right format.
const fireDate = ReactNativeAN.parseDate(notification.date);
const alarm = await ReactNativeAN.scheduleAlarm({ ...alarmNotifData, fire_date: fireDate });
this.logger_.info('AlarmServiceDriver: Created new alarm:', alarm);
}
}

View File

@ -1,17 +1,20 @@
import { Notification } from 'lib/models/Alarm';
import Logger from 'lib/Logger';
const PushNotificationIOS = require('@react-native-community/push-notification-ios').default;
export default class AlarmServiceDriver {
private hasPermission_:boolean = null;
private inAppNotificationHandler_:any = null;
private logger_:Logger;
constructor() {
constructor(logger:Logger) {
this.logger_ = logger;
PushNotificationIOS.addEventListener('localNotification', (instance:any) => {
if (!this.inAppNotificationHandler_) return;
if (!instance || !instance._data || !instance._data.id) {
console.warn('PushNotificationIOS.addEventListener: Did not receive a proper notification instance');
this.logger_.warn('PushNotificationIOS.addEventListener: Did not receive a proper notification instance');
return;
}
@ -57,7 +60,7 @@ export default class AlarmServiceDriver {
return this.hasPermissions(newPerm);
}
async clearNotification(id:any) {
async clearNotification(id:number) {
PushNotificationIOS.cancelLocalNotifications({ id: `${id}` });
}

View File

@ -33,11 +33,11 @@ export default class AlarmServiceDriverNode {
return false;
}
notificationIsSet(id:string) {
notificationIsSet(id:number) {
return id in this.notifications_;
}
async clearNotification(id:string) {
async clearNotification(id:number) {
if (!this.notificationIsSet(id)) return;
shim.clearTimeout(this.notifications_[id].timeoutId);
delete this.notifications_[id];

View File

@ -14,6 +14,7 @@ interface WatchedItem {
lastResourceUpdatedTime: number,
path:string,
asyncSaveQueue: AsyncActionQueue,
size: number,
}
interface WatchedItems {
@ -111,7 +112,12 @@ export default class ResourceEditWatcher {
const stat = await shim.fsDriver().stat(path);
const editedFileUpdatedTime = stat.mtime.getTime();
if (watchedItem.lastFileUpdatedTime === editedFileUpdatedTime) {
// To check if the item has really changed we look at the updated time and size, which
// in most cases is sufficient. It could be a problem if the editing tool is making a change
// that neither changes the timestamp nor the file size. The alternative would be to compare
// the files byte for byte but that could be slow and the file might have changed again by
// the time we finished comparing.
if (watchedItem.lastFileUpdatedTime === editedFileUpdatedTime && watchedItem.size === stat.size) {
// chokidar is buggy and emits "change" events even when nothing has changed
// so double-check the modified time and skip processing if there's no change.
// In particular it emits two such events just after the file has been copied
@ -121,13 +127,14 @@ export default class ResourceEditWatcher {
// handle and once in the "raw" event handler, due to a bug in chokidar. So having
// this check means we don't unecessarily save the resource twice when the file is
// modified by the user.
this.logger().debug(`ResourceEditWatcher: No timestamp change - skip: ${resourceId}`);
this.logger().debug(`ResourceEditWatcher: No timestamp and file size change - skip: ${resourceId}`);
return;
}
this.logger().debug(`ResourceEditWatcher: Queuing save action: ${resourceId}`);
watchedItem.asyncSaveQueue.push(makeSaveAction(resourceId, path));
watchedItem.lastFileUpdatedTime = editedFileUpdatedTime;
watchedItem.size = stat.size;
};
if (!this.watcher_) {
@ -185,6 +192,7 @@ export default class ResourceEditWatcher {
lastResourceUpdatedTime: 0,
asyncSaveQueue: new AsyncActionQueue(1000),
path: '',
size: -1,
};
this.watchedItems_[resourceId] = watchedItem;
@ -200,6 +208,7 @@ export default class ResourceEditWatcher {
watchedItem.path = editFilePath;
watchedItem.lastFileUpdatedTime = stat.mtime.getTime();
watchedItem.lastResourceUpdatedTime = resource.updated_time;
watchedItem.size = stat.size;
this.watch(editFilePath);

View File

@ -9,9 +9,9 @@ const { PermissionsAndroid } = require('react-native');
export default async (sharedData: SharedData, folderId: string, dispatch: Function) => {
if (!!sharedData.resources && sharedData.resources.length > 0) {
const hasPermissions = await checkPermissions(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
if (!hasPermissions) {
if (response !== PermissionsAndroid.RESULTS.GRANTED) {
ToastAndroid.show('Cannot receive shared data - permission denied', ToastAndroid.SHORT);
ShareExtension.close();
return;

View File

@ -1,6 +1,6 @@
const shim = require('lib/shim').default;
const { GeolocationReact } = require('lib/geolocation-react.js');
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
const PoorManIntervals = require('lib/PoorManIntervals').default;
const RNFetchBlob = require('rn-fetch-blob').default;
const { generateSecureRandom } = require('react-native-securerandom');
const FsDriverRN = require('lib/fs-driver-rn.js').FsDriverRN;
@ -18,8 +18,6 @@ const injectedJs = {
function shimInit() {
shim.Geolocation = GeolocationReact;
shim.setInterval = PoorManIntervals.setInterval;
shim.clearInterval = PoorManIntervals.clearInterval;
shim.sjclModule = require('lib/vendor/sjcl-rn.js');
shim.fsDriver = () => {
@ -46,6 +44,13 @@ function shimInit() {
if (!validatedUrl) throw new Error(`Not a valid URL: ${url}`);
return shim.fetchWithRetry(() => {
// If the request has a body and it's not a GET call, and it doesn't have a Content-Type header
// we display a warning, because it could trigger a "Network request failed" error.
// https://github.com/facebook/react-native/issues/30176
if (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) {
console.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options);
}
return fetch(validatedUrl, options);
}, options);
};
@ -199,19 +204,19 @@ function shimInit() {
};
shim.setTimeout = (fn, interval) => {
return setTimeout(fn, interval);
return PoorManIntervals.setTimeout(fn, interval);
};
shim.setInterval = (fn, interval) => {
return setInterval(fn, interval);
return PoorManIntervals.setInterval(fn, interval);
};
shim.clearTimeout = (id) => {
return clearTimeout(id);
return PoorManIntervals.clearTimeout(id);
};
shim.clearInterval = (id) => {
return clearInterval(id);
return PoorManIntervals.clearInterval(id);
};
}

View File

@ -33,6 +33,14 @@ class Time {
this.timeFormat_ = v;
}
use24HourFormat() {
return this.timeFormat() ? this.timeFormat().includes('HH') : true;
}
formatDateToLocal(date, format = null) {
return this.formatMsToLocal(date.getTime(), format);
}
dateTimeFormat() {
return `${this.dateFormat()} ${this.timeFormat()}`;
}

View File

@ -1,36 +0,0 @@
// Note about the application structure:
// - The user interface and its state is managed by React/Redux.
// - Persistent storage to SQLite and Web API is handled outside of React/Redux using regular JavaScript (no middleware, no thunk, etc.).
// - Communication from React to SQLite is done by calling model methods (note.save, etc.)
// - Communication from SQLite to Redux is done via dispatcher.
// So there's basically still a one way flux: React => SQLite => Redux => React
// console.disableYellowBox = true
import { YellowBox, AppRegistry } from 'react-native';
YellowBox.ignoreWarnings([
'Require cycle: node_modules/react-native-',
'Require cycle: node_modules/rn-fetch-blob',
'Warning: componentWillReceiveProps has been renamed',
'Warning: componentWillUpdate has been renamed',
'Warning: componentWillMount has been renamed',
]);
const { Root } = require('./root.js');
// Disable buggy Fast Refresh
// NOTE: not working - can make the app go into an infinite crash/restart loop
// if (__DEV__) {
// const { DevSettings } = NativeModules;
// DevSettings.setHotLoadingEnabled(false);
// DevSettings.setLiveReloadEnabled(false);
// }
function main() {
AppRegistry.registerComponent('Joplin', () => Root);
console.ignoredYellowBox = ['Remote debugger'];
// Note: The final part of the initialization process is in
// AppComponent.componentDidMount(), when the application is ready.
}
module.exports = { main };

View File

@ -4,6 +4,7 @@
*
* @format
*/
module.exports = {
transformer: {
getTransformOptions: async () => ({

File diff suppressed because it is too large Load Diff

View File

@ -5,18 +5,16 @@
"version": "0.8.0",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start --reset-cache",
"start-ios": "react-native run-ios",
"start-android": "react-native run-android",
"postinstall": "patch-package --patch-dir ../patches && jetify && npm run build",
"start": "react-native start --reset-cache",
"build": "gulp build",
"log-ios": "react-native-log-ios \"Joplin\"",
"log-android": "adb logcat *:S ReactNative:V ReactNativeJS:V"
"postinstall": "patch-package --patch-dir ../patches/shared && jetify && npm run build"
},
"dependencies": {
"@react-native-community/clipboard": "^1.5.0",
"@react-native-community/datetimepicker": "^3.0.3",
"@react-native-community/geolocation": "^2.0.2",
"@react-native-community/push-notification-ios": "^1.0.5",
"@react-native-community/slider": "^2.0.8",
"@react-native-community/push-notification-ios": "^1.6.0",
"@react-native-community/slider": "^3.0.3",
"async-mutex": "^0.1.3",
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",
@ -48,7 +46,6 @@
"markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"memory-cache": "^0.2.0",
"mermaid": "^8.8.1",
"moment": "^2.24.0",
"nanoid": "^3.1.12",
@ -56,33 +53,30 @@
"punycode": "^2.1.1",
"query-string": "4.3.4",
"re-reselect": "^4.0.0",
"react": "16.9.0",
"react": "16.13.1",
"react-async": "^10.0.0",
"react-native": "0.61.5",
"react-native-action-button": "^2.6.9",
"react-native-camera": "^2.10.2",
"react-native-datepicker": "^1.6.0",
"react-native-device-info": "^5.5.1",
"react-native": "0.63.3",
"react-native-action-button": "^2.8.5",
"react-native-alarm-notification": "^1.7.1",
"react-native-camera": "^3.40.0",
"react-native-dialogbox": "^0.6.10",
"react-native-document-picker": "^2.3.0",
"react-native-document-picker": "^4.0.0",
"react-native-dropdownalert": "^3.1.2",
"react-native-file-viewer": "^1.0.15",
"react-native-fs": "^2.11.17",
"react-native-image-picker": "^0.26.7",
"react-native-image-resizer": "^1.0.0",
"react-native-log-ios": "^1.5.0",
"react-native-material-dropdown": "^0.5.2",
"react-native-popup-dialog": "^0.9.35",
"react-native-file-viewer": "^2.1.4",
"react-native-fs": "^2.16.6",
"react-native-image-picker": "^2.3.4",
"react-native-image-resizer": "^1.3.0",
"react-native-modal-datetime-picker": "^9.0.0",
"react-native-popup-dialog": "^0.9.41",
"react-native-popup-menu": "^0.10.0",
"react-native-push-notification": "git+https://github.com/laurent22/react-native-push-notification.git",
"react-native-quick-actions": "^0.3.13",
"react-native-securerandom": "^1.0.0-rc.0",
"react-native-share": "^3.3.3",
"react-native-share": "^4.0.2",
"react-native-side-menu": "^1.1.3",
"react-native-sqlite-storage": "^4.1.0",
"react-native-vector-icons": "^6.6.0",
"react-native-version-info": "^1.0.1",
"react-native-webview": "^5.12.0",
"react-native-sqlite-storage": "^5.0.0",
"react-native-vector-icons": "^7.1.0",
"react-native-version-info": "^1.1.0",
"react-native-webview": "^10.9.2",
"react-redux": "5.0.7",
"redux": "4.0.0",
"reselect": "^4.0.0",
@ -100,15 +94,14 @@
"xml2js": "^0.4.19"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/runtime": "^7.6.2",
"@babel/core": "^7.11.6",
"@babel/runtime": "^7.11.2",
"app-module-path": "^2.2.0",
"execa": "^4.0.0",
"fs-extra": "^8.1.0",
"gulp": "^4.0.2",
"jetifier": "^1.6.5",
"metro-react-native-babel-preset": "^0.54.1",
"patch-package": "^6.2.2",
"react-test-renderer": "^16.8.3"
"metro-react-native-babel-preset": "^0.63.0",
"patch-package": "^6.2.2"
}
}

View File

@ -0,0 +1 @@
module.exports = `LyoKCkF0b20gT25lIERhcmsgV2l0aCBzdXBwb3J0IGZvciBSZWFzb25NTCBieSBHaWRpIE1vcnJpcywgYmFzZWQgb2ZmIHdvcmsgYnkgRGFuaWVsIEdhbWFnZQoKT3JpZ2luYWwgT25lIERhcmsgU3ludGF4IHRoZW1lIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2F0b20vb25lLWRhcmstc3ludGF4CgoqLwouaGxqcyB7CiAgZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsKICBjb2xvcjogI2FiYjJiZjsKICBiYWNrZ3JvdW5kOiAjMjgyYzM0Owp9Ci5obGpzLWtleXdvcmQsIC5obGpzLW9wZXJhdG9yIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIC5obGpzLWNvbnN0cnVjdG9yIHsKICBjb2xvcjogIzYxYWVlZTsKfQouaGxqcy1mdW5jdGlvbiB7CiAgY29sb3I6ICM2MWFlZWU7Cn0KLmhsanMtZnVuY3Rpb24gLmhsanMtcGFyYW1zIHsKICBjb2xvcjogI0E2RTIyRTsKfQouaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5nIHsKICBjb2xvcjogI0ZEOTcxRjsKfQouaGxqcy1tb2R1bGUtYWNjZXNzIC5obGpzLW1vZHVsZSB7CiAgY29sb3I6ICM3ZTU3YzI7Cn0KLmhsanMtY29uc3RydWN0b3IgewogIGNvbG9yOiAjZTJiOTNkOwp9Ci5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZyB7CiAgY29sb3I6ICM5Q0NDNjU7Cn0KLmhsanMtY29tbWVudCwgLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYjE4ZWIxOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQouaGxqcy1kb2N0YWcsIC5obGpzLWZvcm11bGEgewogIGNvbG9yOiAjYzY3OGRkOwp9Ci5obGpzLXNlY3Rpb24sIC5obGpzLW5hbWUsIC5obGpzLXNlbGVjdG9yLXRhZywgLmhsanMtZGVsZXRpb24sIC5obGpzLXN1YnN0IHsKICBjb2xvcjogI2UwNmM3NTsKfQouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzU2YjZjMjsKfQouaGxqcy1zdHJpbmcsIC5obGpzLXJlZ2V4cCwgLmhsanMtYWRkaXRpb24sIC5obGpzLWF0dHJpYnV0ZSwgLmhsanMtbWV0YS1zdHJpbmcgewogIGNvbG9yOiAjOThjMzc5Owp9Ci5obGpzLWJ1aWx0X2luLCAuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSB7CiAgY29sb3I6ICNlNmMwN2I7Cn0KLmhsanMtYXR0ciwgLmhsanMtdmFyaWFibGUsIC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLCAuaGxqcy10eXBlLCAuaGxqcy1zZWxlY3Rvci1jbGFzcywgLmhsanMtc2VsZWN0b3ItYXR0ciwgLmhsanMtc2VsZWN0b3ItcHNldWRvLCAuaGxqcy1udW1iZXIgewogIGNvbG9yOiAjZDE5YTY2Owp9Ci5obGpzLXN5bWJvbCwgLmhsanMtYnVsbGV0LCAuaGxqcy1saW5rLCAuaGxqcy1tZXRhLCAuaGxqcy1zZWxlY3Rvci1pZCwgLmhsanMtdGl0bGUgewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQouaGxqcy1saW5rIHsKICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKfQo=`;

View File

@ -0,0 +1 @@
module.exports = `LyoKCkF0b20gT25lIExpZ2h0IGJ5IERhbmllbCBHYW1hZ2UKT3JpZ2luYWwgT25lIExpZ2h0IFN5bnRheCB0aGVtZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9hdG9tL29uZS1saWdodC1zeW50YXgKCmJhc2U6ICAgICNmYWZhZmEKbW9uby0xOiAgIzM4M2E0Mgptb25vLTI6ICAjNjg2Yjc3Cm1vbm8tMzogICNhMGExYTcKaHVlLTE6ICAgIzAxODRiYgpodWUtMjogICAjNDA3OGYyCmh1ZS0zOiAgICNhNjI2YTQKaHVlLTQ6ICAgIzUwYTE0ZgpodWUtNTogICAjZTQ1NjQ5Cmh1ZS01LTI6ICNjOTEyNDMKaHVlLTY6ICAgIzk4NjgwMQpodWUtNi0yOiAjYzE4NDAxCgoqLwoKLmhsanMgewogIGRpc3BsYXk6IGJsb2NrOwogIG92ZXJmbG93LXg6IGF1dG87CiAgcGFkZGluZzogMC41ZW07CiAgY29sb3I6ICMzODNhNDI7CiAgYmFja2dyb3VuZDogI2ZhZmFmYTsKfQoKLmhsanMtY29tbWVudCwKLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYTBhMWE3OwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtZG9jdGFnLAouaGxqcy1rZXl3b3JkLAouaGxqcy1mb3JtdWxhIHsKICBjb2xvcjogI2E2MjZhNDsKfQoKLmhsanMtc2VjdGlvbiwKLmhsanMtbmFtZSwKLmhsanMtc2VsZWN0b3ItdGFnLAouaGxqcy1kZWxldGlvbiwKLmhsanMtc3Vic3QgewogIGNvbG9yOiAjZTQ1NjQ5Owp9CgouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzAxODRiYjsKfQoKLmhsanMtc3RyaW5nLAouaGxqcy1yZWdleHAsCi5obGpzLWFkZGl0aW9uLAouaGxqcy1hdHRyaWJ1dGUsCi5obGpzLW1ldGEtc3RyaW5nIHsKICBjb2xvcjogIzUwYTE0ZjsKfQoKLmhsanMtYnVpbHRfaW4sCi5obGpzLWNsYXNzIC5obGpzLXRpdGxlIHsKICBjb2xvcjogI2MxODQwMTsKfQoKLmhsanMtYXR0ciwKLmhsanMtdmFyaWFibGUsCi5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLAouaGxqcy10eXBlLAouaGxqcy1zZWxlY3Rvci1jbGFzcywKLmhsanMtc2VsZWN0b3ItYXR0ciwKLmhsanMtc2VsZWN0b3ItcHNldWRvLAouaGxqcy1udW1iZXIgewogIGNvbG9yOiAjOTg2ODAxOwp9CgouaGxqcy1zeW1ib2wsCi5obGpzLWJ1bGxldCwKLmhsanMtbGluaywKLmhsanMtbWV0YSwKLmhsanMtc2VsZWN0b3ItaWQsCi5obGpzLXRpdGxlIHsKICBjb2xvcjogIzQwNzhmMjsKfQoKLmhsanMtZW1waGFzaXMgewogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoKLmhsanMtbGluayB7CiAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7Cn0K`;

View File

@ -0,0 +1,29 @@
module.exports = {
hash:"0f24ccb2b0c004ee83a88554e1d7bf62", files: {
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Caligraphic-Bold.woff2': { data: require('./katex/fonts/KaTeX_Caligraphic-Bold.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Caligraphic-Regular.woff2': { data: require('./katex/fonts/KaTeX_Caligraphic-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Fraktur-Bold.woff2': { data: require('./katex/fonts/KaTeX_Fraktur-Bold.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Fraktur-Regular.woff2': { data: require('./katex/fonts/KaTeX_Fraktur-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Main-Bold.woff2': { data: require('./katex/fonts/KaTeX_Main-Bold.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Main-BoldItalic.woff2': { data: require('./katex/fonts/KaTeX_Main-BoldItalic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Main-Italic.woff2': { data: require('./katex/fonts/KaTeX_Main-Italic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Main-Regular.woff2': { data: require('./katex/fonts/KaTeX_Main-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Math-BoldItalic.woff2': { data: require('./katex/fonts/KaTeX_Math-BoldItalic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Math-Italic.woff2': { data: require('./katex/fonts/KaTeX_Math-Italic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_SansSerif-Bold.woff2': { data: require('./katex/fonts/KaTeX_SansSerif-Bold.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_SansSerif-Italic.woff2': { data: require('./katex/fonts/KaTeX_SansSerif-Italic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_SansSerif-Regular.woff2': { data: require('./katex/fonts/KaTeX_SansSerif-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Script-Regular.woff2': { data: require('./katex/fonts/KaTeX_Script-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Size1-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size1-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Size2-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size2-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Size3-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size3-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Size4-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size4-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/fonts/KaTeX_Typewriter-Regular.woff2': { data: require('./katex/fonts/KaTeX_Typewriter-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
'katex/katex.css': { data: require('./katex/katex.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'mermaid/mermaid.min.js': { data: require('./mermaid/mermaid.min.js.base64.js'), mime: 'application/javascript', encoding: 'base64' },
'mermaid/mermaid_render.js': { data: require('./mermaid/mermaid_render.js.base64.js'), mime: 'application/javascript', encoding: 'base64' },
}
};

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