Merge pull request #153 from standardnotes/3.0.6

3.0.6
This commit is contained in:
Mo Bitar
2019-05-15 19:17:22 -05:00
committed by GitHub
42 changed files with 443 additions and 2857 deletions

View File

@@ -102,8 +102,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 3000050
versionName "3.0.5"
versionCode 3000060
versionName "3.0.6"
multiDexEnabled true
@@ -159,6 +159,8 @@ android {
}
dependencies {
compile project(':@react-native-community_async-storage')
compile project(':react-native-webview')
compile project(':react-native-file-viewer')
compile project(':react-native-fs')
compile project(':react-native-gesture-handler')

View File

@@ -9,6 +9,8 @@ import android.support.annotation.Nullable;
import android.view.WindowManager;
import com.facebook.react.ReactApplication;
import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
import com.rnfs.RNFSPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
@@ -45,18 +47,20 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNFileViewerPackage(),
new RNFSPackage(),
new RNGestureHandlerPackage(),
BugsnagReactNative.getPackage(),
new KeychainPackage(),
new VectorIconsPackage(),
new RCTAesPackage(),
new RNMail(),
new ReactNativeFingerprintScannerPackage(),
new SNTextViewPackage(),
new FlagSecurePackage()
new MainReactPackage(),
new AsyncStoragePackage(),
new RNCWebViewPackage(),
new RNFileViewerPackage(),
new RNFSPackage(),
new RNGestureHandlerPackage(),
BugsnagReactNative.getPackage(),
new KeychainPackage(),
new VectorIconsPackage(),
new RCTAesPackage(),
new RNMail(),
new ReactNativeFingerprintScannerPackage(),
new SNTextViewPackage(),
new FlagSecurePackage()
);
}
@@ -77,10 +81,6 @@ public class MainApplication extends Application implements ReactApplication {
SoLoader.init(this, /* native exopackage */ false);
// Set AsyncStorage size, default is 6mb
long size = 50L * 1024L * 1024L; // 50 MB
com.facebook.react.modules.storage.ReactDatabaseSupplier.getInstance(getApplicationContext()).setMaximumSize(size);
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

View File

@@ -17,4 +17,6 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true
AsyncStorage_db_size_in_MB=50
#android.enableAapt2=false

View File

@@ -1,4 +1,8 @@
rootProject.name = 'StandardNotes'
include ':@react-native-community_async-storage'
project(':@react-native-community_async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
include ':react-native-file-viewer'
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')
include ':react-native-fs'

View File

@@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@@ -38,6 +37,7 @@
300BF5D7132F46BCAB353149 /* libRNGestureHandler.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04047F33889C425483EB8244 /* libRNGestureHandler.a */; };
3B66E39D04514331AF08A4D1 /* FontAwesome5_Solid.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 074A7613CE8C41BAB3C4193E /* FontAwesome5_Solid.ttf */; };
40F325481D4F4F04AC15D0A9 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 48127930FB1344778C168838 /* libRNVectorIcons.a */; };
4C4C0C80E5254E16BA086A86 /* libRNCWebView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4449457EB3FC4387810027F1 /* libRNCWebView.a */; };
5A8928DC2824482A99763067 /* FontAwesome5_Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EF2E9E34ADA441EDB4CE8370 /* FontAwesome5_Regular.ttf */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
6C1915C4DE9040A9BB17CFBB /* libRNStoreReview.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADB6F6B9BC7144FCB2C08D40 /* libRNStoreReview.a */; };
@@ -58,6 +58,7 @@
CDB58A211F6C51A4009EF868 /* libReactNativeFingerprintScanner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A0F1F6C5174009EF868 /* libReactNativeFingerprintScanner.a */; };
CDB58A221F6C5235009EF868 /* libRCTAes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A091F6C516B009EF868 /* libRCTAes.a */; };
F0D84FDB75374348BC0017C2 /* libBugsnagReactNative.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFD3F9197A5F41C0904D7E60 /* libBugsnagReactNative.a */; };
7A0B269BD1714EA79E486BAE /* libRNCAsyncStorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1009A2994551440E819E2CE6 /* libRNCAsyncStorage.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -327,6 +328,13 @@
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNStoreReview;
};
CD5AEDE322825CB8002439FB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = EC178A0479CE4154896293C3 /* RNCWebView.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNCWebView;
};
CD885F771F70228400B91C20 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 737AF67874434967865855D8 /* BugsnagReactNative.xcodeproj */;
@@ -484,6 +492,7 @@
2D02E4901E0B4A5D006451C7 /* StandardNotes-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "StandardNotes-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
2FAF1266E8404686B7F36870 /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; };
37A42F4068AE42DD8D2DF182 /* libRNFileViewer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFileViewer.a; sourceTree = "<group>"; };
4449457EB3FC4387810027F1 /* libRNCWebView.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCWebView.a; sourceTree = "<group>"; };
48127930FB1344778C168838 /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = "<group>"; };
54ED130E749A46A3B15B27F2 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
59DCF8530F2945FFAA0300FD /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
@@ -511,8 +520,11 @@
CDCF33ADCFE845D588CC4E66 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNKeychain.a; sourceTree = "<group>"; };
CEB6B877AE784055A8E294A8 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = "<group>"; };
D38724A93AC141D6B51D1356 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
EC178A0479CE4154896293C3 /* RNCWebView.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNCWebView.xcodeproj; path = "../node_modules/react-native-webview/ios/RNCWebView.xcodeproj"; sourceTree = "<group>"; };
EF2E9E34ADA441EDB4CE8370 /* FontAwesome5_Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Regular.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf"; sourceTree = "<group>"; };
EFD3F9197A5F41C0904D7E60 /* libBugsnagReactNative.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBugsnagReactNative.a; sourceTree = "<group>"; };
FF655164AD874250A8F6E20E /* RNCAsyncStorage.xcodeproj */ = {isa = PBXFileReference; name = "RNCAsyncStorage.xcodeproj"; path = "../node_modules/@react-native-community/async-storage/ios/RNCAsyncStorage.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
1009A2994551440E819E2CE6 /* libRNCAsyncStorage.a */ = {isa = PBXFileReference; name = "libRNCAsyncStorage.a"; path = "libRNCAsyncStorage.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -554,6 +566,8 @@
300BF5D7132F46BCAB353149 /* libRNGestureHandler.a in Frameworks */,
0A7C7EB08AA3465C8E686821 /* libRNFS.a in Frameworks */,
9E1F5D0FE7C441D685DAEEAA /* libRNFileViewer.a in Frameworks */,
4C4C0C80E5254E16BA086A86 /* libRNCWebView.a in Frameworks */,
7A0B269BD1714EA79E486BAE /* libRNCAsyncStorage.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -769,6 +783,8 @@
19A39FBB53A3465B81022E02 /* RNGestureHandler.xcodeproj */,
2FAF1266E8404686B7F36870 /* RNFS.xcodeproj */,
645E3D9167344C3E81B7223C /* RNFileViewer.xcodeproj */,
EC178A0479CE4154896293C3 /* RNCWebView.xcodeproj */,
FF655164AD874250A8F6E20E /* RNCAsyncStorage.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@@ -842,6 +858,14 @@
name = Products;
sourceTree = "<group>";
};
CD5AEDE022825CB8002439FB /* Products */ = {
isa = PBXGroup;
children = (
CD5AEDE422825CB8002439FB /* libRNCWebView.a */,
);
name = Products;
sourceTree = "<group>";
};
CD885F741F70228400B91C20 /* Products */ = {
isa = PBXGroup;
children = (
@@ -902,6 +926,7 @@
04047F33889C425483EB8244 /* libRNGestureHandler.a */,
CEB6B877AE784055A8E294A8 /* libRNFS.a */,
37A42F4068AE42DD8D2DF182 /* libRNFileViewer.a */,
4449457EB3FC4387810027F1 /* libRNCWebView.a */,
);
name = "Recovered References";
sourceTree = "<group>";
@@ -1127,6 +1152,10 @@
ProductGroup = CDB58A0B1F6C5174009EF868 /* Products */;
ProjectRef = CDB58A0A1F6C5174009EF868 /* ReactNativeFingerprintScanner.xcodeproj */;
},
{
ProductGroup = CD5AEDE022825CB8002439FB /* Products */;
ProjectRef = EC178A0479CE4154896293C3 /* RNCWebView.xcodeproj */;
},
{
ProductGroup = CDEBDDB721E044BD00333D77 /* Products */;
ProjectRef = 645E3D9167344C3E81B7223C /* RNFileViewer.xcodeproj */;
@@ -1423,6 +1452,13 @@
remoteRef = CD4D91841F7BE11800080678 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
CD5AEDE422825CB8002439FB /* libRNCWebView.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRNCWebView.a;
remoteRef = CD5AEDE322825CB8002439FB /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
CD885F781F70228400B91C20 /* libBugsnagReactNative.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -1696,6 +1732,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = StandardNotesTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1704,11 +1742,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1734,6 +1767,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = StandardNotesTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1742,11 +1777,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1776,6 +1806,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = StandardNotes/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1811,6 +1843,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = StandardNotes/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1849,6 +1883,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = "StandardNotes-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1856,11 +1892,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1895,6 +1926,8 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-fs/**",
"$(SRCROOT)/../node_modules/react-native-file-viewer/ios",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/@react-native-community/async-storage/ios",
);
INFOPLIST_FILE = "StandardNotes-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1902,11 +1935,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1937,11 +1965,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.StandardNotes-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1968,11 +1991,6 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.StandardNotes-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -67,11 +67,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.5</string>
<string>3.0.6</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>5</string>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@@ -1,54 +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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</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

@@ -1,24 +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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</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

File diff suppressed because it is too large Load Diff

View File

@@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D2A28121D9B038B00D4039D"
BuildableName = "libReact.a"
BlueprintName = "React-tvOS"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "StandardNotes-tvOS.app"
BlueprintName = "StandardNotes-tvOS"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "StandardNotes-tvOSTests.xctest"
BlueprintName = "StandardNotes-tvOSTests"
ReferencedContainer = "container:StandardNotes.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 = "StandardNotes-tvOSTests.xctest"
BlueprintName = "StandardNotes-tvOSTests"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "StandardNotes-tvOS.app"
BlueprintName = "StandardNotes-tvOS"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</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 = "StandardNotes-tvOS.app"
BlueprintName = "StandardNotes-tvOS"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "StandardNotes-tvOS.app"
BlueprintName = "StandardNotes-tvOS"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
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"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "sn_react.app"
BlueprintName = "StandardNotes"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "StandardNotesTests.xctest"
BlueprintName = "StandardNotesTests"
ReferencedContainer = "container:StandardNotes.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 = "00E356ED1AD99517003FC87E"
BuildableName = "StandardNotesTests.xctest"
BlueprintName = "StandardNotesTests"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "sn_react.app"
BlueprintName = "StandardNotes"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</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 = "13B07F861A680F5B00A75B9A"
BuildableName = "sn_react.app"
BlueprintName = "StandardNotes"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "sn_react.app"
BlueprintName = "StandardNotes"
ReferencedContainer = "container:StandardNotes.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* 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>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* 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 <React/RCTBundleURLProvider.h>
#import "RCCManager.h"
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
// RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
// moduleName:@"sn_react"
// initialProperties:nil
// launchOptions:launchOptions];
// rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
// self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// UIViewController *rootViewController = [UIViewController new];
// rootViewController.view = rootView;
// self.window.rootViewController = rootViewController;
// [self.window makeKeyAndVisible];
return YES;
}
@end

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16G23a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</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"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,68 +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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>S. Notes</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<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>UIAppFonts</key>
<array>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>FontAwesome.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>Octicons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Zocial.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@@ -1,18 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* 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"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -1,10 +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>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.reactjs.native.example.sn-react</string>
</array>
</dict>
</plist>

View File

@@ -1,24 +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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</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

@@ -1,70 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* 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 <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface sn_reactTests : XCTestCase
@end
@implementation sn_reactTests
- (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;
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
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;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end

32
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "StandardNotes",
"version": "3.0.5",
"version": "3.0.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2375,6 +2375,11 @@
}
}
},
"@react-native-community/async-storage": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.4.0.tgz",
"integrity": "sha512-Aksg16keqrxaluFRZwmo8O8ppP9TFylyCEwBElmxeZ+a6DQAvyMn5nS3n+lgSpkYsrwU2ZGVjDluhkjtBrkEqQ=="
},
"@react-navigation/core": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.0.2.tgz",
@@ -10634,6 +10639,15 @@
}
}
},
"react-native-webview": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-5.8.1.tgz",
"integrity": "sha512-b6pSvmjoiWtcz6YspggW02X+BRXJWuquHwkh37BRx1NMW1iwMZA31SnFQvTpPzWYYIb9WF/mRsy2nGtt9C6NIg==",
"requires": {
"escape-string-regexp": "1.0.5",
"invariant": "2.2.4"
}
},
"react-navigation": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.0.9.tgz",
@@ -11543,11 +11557,6 @@
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
},
"sn-models": {
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/sn-models/-/sn-models-0.1.14.tgz",
"integrity": "sha512-p5Tp18sKP68saA4EkdAeYo+X3dNdmGwgqPKL0Bi0Y6lv2SO0LiHPbcBxKcsQjrikE8ePUviJVeckRfMgR5QbYA=="
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -11645,6 +11654,11 @@
"kind-of": "3.2.2"
}
},
"snjs": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/snjs/-/snjs-0.2.0.tgz",
"integrity": "sha512-wkvycOiFKnEewyFDlfN5k+zu3ppvzCdE9YQPR82dWMqwxZK3qCx8RuBGr2p0M4bjyqrPXyVwVrJHbE60kGVRug=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -11746,9 +11760,9 @@
"integrity": "sha1-ATl5IuX2Ls8whFUiyVxP4dJefU4="
},
"standard-file-js": {
"version": "0.3.56",
"resolved": "https://registry.npmjs.org/standard-file-js/-/standard-file-js-0.3.56.tgz",
"integrity": "sha512-I/iOm53skG+4NsbKdzmGSra6fEj9Qm0xirhUAToaLyXpbWkuQMu+md6SMEJO8Bg9cFmjUhAk1xtxVr3+IPf/eA=="
"version": "0.3.58",
"resolved": "https://registry.npmjs.org/standard-file-js/-/standard-file-js-0.3.58.tgz",
"integrity": "sha512-xkoC+lDK12WPL2+ckD2Ki8WfTsy8TphvwqLS9OASuY06MHdVh9qGGooYvewjXSQkSQTBDrs7f+0mzEX0Rn2AZw=="
},
"static-extend": {
"version": "0.1.2",

View File

@@ -1,8 +1,8 @@
{
"name": "StandardNotes",
"version": "3.0.5",
"versionIOS": "3.0.5",
"versionAndroid": "3.0.5",
"version": "3.0.6",
"versionIOS": "3.0.6",
"versionAndroid": "3.0.6",
"license": "AGPL-3.0-or-later",
"private": true,
"scripts": {
@@ -12,6 +12,7 @@
"test": "jest"
},
"dependencies": {
"@react-native-community/async-storage": "1.4.0",
"base-64": "^0.1.0",
"bugsnag-react-native": "^2.12.6",
"immutable": "^3.8.1",
@@ -25,11 +26,12 @@
"react-native-keychain": "^1.2.1",
"react-native-store-review": "^0.1.3",
"react-native-vector-icons": "6.1.0",
"react-native-webview": "5.8.1",
"react-navigation": "^3.0.9",
"react-navigation-header-buttons": "^2.1.1",
"regenerator": "^0.13.3",
"sn-models": "0.1.14",
"standard-file-js": "0.3.56"
"snjs": "0.2.0",
"standard-file-js": "0.3.58"
},
"devDependencies": {
"babel-jest": "^23.6.0",

View File

@@ -45,10 +45,12 @@ import {
SNNote,
SNTag,
SNTheme,
SNComponent
} from 'sn-models';
SNComponent,
SNComponentManager
} from 'snjs';
global.SNNote = SNNote;
global.SNTag = SNTag;
global.SNTheme = SNTheme;
global.SNComponent = SNComponent;
global.SNComponentManager = SNComponentManager;

View File

@@ -1,412 +1,71 @@
/* This domain will be used to save context item client data */
let ClientDataDomain = "org.standardnotes.sn.components";
import ModelManager from "@SFJS/modelManager";
import Sync from "@SFJS/syncManager";
import AlertManager from "@SFJS/alertManager";
import { Platform } from 'react-native';
import StyleKit from "@Style/StyleKit"
import { Platform, Alert } from 'react-native';
import StyleKit from '@Style/StyleKit'
import ModelManager from './sfjs/modelManager'
import Sync from './sfjs/syncManager'
import SF from "./sfjs/sfjs"
import ApplicationState from "@Lib/ApplicationState"
export default class ComponentManager {
export default class ComponentManager extends SNComponentManager {
static instance = null;
static get() {
if (this.instance == null) {
this.instance = new ComponentManager();
this.instance = new ComponentManager({
modelManager: ModelManager.get(),
syncManager: Sync.get(),
alertManager: AlertManager.get(),
environment: "mobile",
platform: Platform.OS
});
}
return this.instance;
}
constructor() {
this.streamObservers = [];
this.contextStreamObservers = [];
this.activeComponents = [];
// this.loggingEnabled = true;
this.handlers = [];
StyleKit.get().addThemeChangeObserver(() => {
this.postActiveThemesToAllComponents();
});
ModelManager.get().addItemSyncObserver("component-manager", "*", (allItems, validItems, deletedItems, source) => {
/* If the source of these new or updated items is from a Component itself saving items, we don't need to notify
components again of the same item. Regarding notifying other components than the issuing component, other mapping sources
will take care of that, like ModelManager.MappingSourceRemoteSaved
*/
if(source == SFModelManager.MappingSourceComponentRetrieved) {
return;
}
for(let observer of this.contextStreamObservers) {
for(let handler of this.handlers) {
if(!handler.areas.includes(observer.component.area) && !handler.areas.includes("*")) {
continue;
}
if(handler.contextRequestHandler) {
var itemInContext = handler.contextRequestHandler(observer.component);
if(itemInContext) {
var matchingItem = _.find(allItems, {uuid: itemInContext.uuid});
if(matchingItem) {
this.sendContextItemInReply(observer.component, matchingItem, observer.originalMessage, source);
}
}
}
}
}
});
constructor({modelManager, syncManager, desktopManager, nativeExtManager,
alertManager, $uiRunner, platform, environment}) {
super({modelManager, syncManager, desktopManager, nativeExtManager,
alertManager, $uiRunner, platform, environment});
}
contextItemDidChangeInArea(area) {
for(let handler of this.handlers) {
if(handler.areas.includes(area) === false && !handler.areas.includes("*")) {
continue;
}
var observers = this.contextStreamObservers.filter(function(observer){
return observer.component.area === area;
})
/*
Overrides
*/
for(let observer of observers) {
if(handler.contextRequestHandler) {
var itemInContext = handler.contextRequestHandler(observer.component);
this.sendContextItemInReply(observer.component, itemInContext, observer.originalMessage);
}
}
}
}
jsonForItem(item, component, source) {
var params = {uuid: item.uuid, content_type: item.content_type, created_at: item.created_at, updated_at: item.updated_at, deleted: item.deleted};
params.content = item.createContentJSONFromProperties();
params.clientData = item.getDomainDataItem(component.getClientDataKey(), ClientDataDomain) || {};
/* This means the this function is being triggered through a remote Saving response, which should not update
actual local content values. The reason is, Save responses may be delayed, and a user may have changed some values
in between the Save was initiated, and the time it completes. So we only want to update actual content values (and not just metadata)
when its another source, like ModelManager.MappingSourceRemoteRetrieved.
3/7/18: Add MappingSourceLocalSaved as well to handle fully offline saving. github.com/standardnotes/forum/issues/169
*/
if(source && (source == SFModelManager.MappingSourceRemoteSaved || source == SFModelManager.MappingSourceLocalSaved)) {
params.isMetadataUpdate = true;
}
this.removePrivatePropertiesFromResponseItems([params], component);
return params;
}
sendItemsInReply(component, items, message, source) {
if(this.loggingEnabled) {console.log("Web|componentManager|sendItemsInReply", component.name, items, message)};
var response = {items: {}};
var mapped = items.map(function(item) {
return this.jsonForItem(item, component, source);
}.bind(this));
response.items = mapped;
this.replyToMessage(component, message, response);
}
sendContextItemInReply(component, item, originalMessage, source) {
if(this.loggingEnabled) {console.log("Web|componentManager|sendContextItemInReply", component.name, item, originalMessage)};
var response = {item: this.jsonForItem(item, component, source)};
this.replyToMessage(component, originalMessage, response);
}
replyToMessage(component, originalMessage, replyData) {
var reply = {
action: "reply",
original: originalMessage,
data: replyData
}
this.sendMessageToComponent(component, reply);
}
sendMessageToComponent(component, message) {
let permissibleActionsWhileHidden = ["component-registered", "themes"];
if(component.hidden && !permissibleActionsWhileHidden.includes(message.action)) {
if(this.loggingEnabled) {
console.log("Component disabled for current item, not sending any messages.", component.name);
}
return;
}
if(this.loggingEnabled) {
console.log("Web|sendMessageToComponent", component.name, JSON.stringify(message));
}
component.window.postMessage(JSON.stringify(message));
}
get components() {
return ModelManager.get().allItemsMatchingTypes(["SN|Component", "SN|Theme"]);
}
componentsForArea(area) {
return this.components.filter(function(component){
return component.area === area;
})
}
urlForComponent(component) {
var localReplacement = ApplicationState.isIOS ? "localhost" : "10.0.2.2";
var url = component.hosted_url || component.url;
if(url) {
url = url.replace("localhost", localReplacement).replace("sn.local", localReplacement);
}
return url;
}
componentForUrl(url) {
return this.components.filter(function(component){
return component.url === url || component.hosted_url === url;
})[0];
}
componentForSessionKey(key) {
return _.find(this.components, {sessionKey: key});
}
isReadOnlyMessage(message) {
let writeActions = ["save-items", "delete-items", "create-item"]
// Ensure the message action is not one of the writeActions
return !writeActions.includes(message.action);
}
handleMessage(component, message) {
if(!component) {
if(this.loggingEnabled) {
console.log("Component not defined, returning");
}
return;
}
// Actions that won't succeeed with readonly mode
let readwriteActions = [
"save-items",
"associate-item",
"deassociate-item",
"create-item",
"create-items",
"delete-items",
"set-component-data"
];
if(component.readonly && readwriteActions.includes(message.action)) {
Alert.alert('Extended Expired', `The extension ${component.name} is trying to save, but it is in a locked state and cannot accept changes.`);
return;
}
/*
Mobile only handles a subset of possible messages.
Possible Messages:
set-component-data
stream-context-item
save-items
*/
if(message.action === "stream-context-item") {
this.handleStreamContextItemMessage(component, message);
} else if(message.action === "set-component-data") {
this.handleSetComponentDataMessage(component, message);
} else if(message.action === "save-items") {
this.handleSaveItemsMessage(component, message);
}
// Notify observers
for(let handler of this.handlers) {
if(handler.areas.includes(component.area) || handler.areas.includes("*")) {
setTimeout(function () {
handler.actionHandler && handler.actionHandler(component, message.action, message.data);
}, 10);
}
}
}
removePrivatePropertiesFromResponseItems(responseItems, component, options = {}) {
// Don't allow component to overwrite these properties.
var privateProperties = ["autoupdateDisabled", "permissions", "active"];
if(options) {
if(options.includeUrls) { privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"])}
}
for(var responseItem of responseItems) {
// Do not pass in actual items here, otherwise that would be destructive.
// Instead, generic JS/JSON objects should be passed.
for(var prop of privateProperties) {
delete responseItem.content[prop];
}
}
}
handleStreamContextItemMessage(component, message) {
if(!_.find(this.contextStreamObservers, {identifier: component.uuid})) {
// for pushing laster as changes come in
this.contextStreamObservers.push({
identifier: component.uuid,
component: component,
originalMessage: message
})
}
// push immediately now
for(let handler of this.handlersForArea(component.area)) {
if(handler.contextRequestHandler) {
var itemInContext = handler.contextRequestHandler(component);
this.sendContextItemInReply(component, itemInContext, message);
}
}
}
isItemWithinComponentContextJurisdiction(item, component) {
for(let handler of this.handlersForArea(component.area)) {
if(handler.contextRequestHandler) {
var itemInContext = handler.contextRequestHandler(component);
if(itemInContext && itemInContext.uuid == item.uuid) {
return true;
}
}
}
return false;
}
handlersForArea(area) {
return this.handlers.filter((candidate) => {return candidate.areas.includes(area)});
}
handleSaveItemsMessage(component, message) {
var responseItems = message.data.items;
// Ensure you're just trying to save the context item
if(!(responseItems.length == 1 && this.isItemWithinComponentContextJurisdiction(responseItems[0], component))) {
return;
}
this.removePrivatePropertiesFromResponseItems(responseItems, component, {includeUrls: true});
/*
We map the items here because modelManager is what updates the UI. If you were to instead get the items directly,
this would update them server side via sync, but would never make its way back to the UI.
*/
var localItems = ModelManager.get().mapResponseItemsToLocalModels(responseItems, SFModelManager.MappingSourceComponentRetrieved);
for(var item of localItems) {
var responseItem = _.find(responseItems, {uuid: item.uuid});
_.merge(item.content, responseItem.content);
if(responseItem.clientData) {
item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain);
}
item.setDirty(true);
}
Sync.get().sync().then((response) => {
// Allow handlers to be notified when a save begins and ends, to update the UI
var saveMessage = Object.assign({}, message);
saveMessage.action = (response && response.error) ? "save-error" : "save-success";
this.replyToMessage(component, message, {error: response && response.error})
this.handleMessage(component, saveMessage);
});
}
handleSetComponentDataMessage(component, message) {
component.componentData = message.data.componentData;
component.setDirty(true);
Sync.get().sync();
}
registerHandler(handler) {
this.handlers.push(handler);
}
deregisterHandler(identifier) {
var handler = _.find(this.handlers, {identifier: identifier});
this.handlers.splice(this.handlers.indexOf(handler), 1);
}
// Called by other views when the iframe is ready
async registerComponentWindow(component, componentWindow) {
if(!component) { console.error("component is null");}
if(!componentWindow) { console.error("componentWindow is null");}
if(component.window === componentWindow) {
if(this.loggingEnabled) {
console.log("Web|componentManager", "attempting to re-register same component window.")
}
}
if(this.loggingEnabled) {
console.log("Web|componentManager|registerComponentWindow");
}
component.window = componentWindow;
component.sessionKey = await SF.get().crypto.generateUUID();
this.sendMessageToComponent(component, {
action: "component-registered",
sessionKey: component.sessionKey,
componentData: component.componentData,
data: {
uuid: component.uuid,
environment: "mobile",
platform: Platform.OS,
activeThemeUrls: [this.getActiveThemeUrl()]
}
});
// Some editors may not yet accept initial activeThemeUrls in component-registerd event
this.postActiveThemeToComponent(component);
}
deactivateComponent(component, dontSync = false) {
component.active = false;
component.sessionKey = null;
this.streamObservers = this.streamObservers.filter(function(o){
return o.component !== component;
})
this.contextStreamObservers = this.contextStreamObservers.filter(function(o){
return o.component !== component;
})
}
getActiveThemeUrl() {
urlsForActiveThemes() {
let theme = StyleKit.get().activeTheme;
if(theme.content.isSystemTheme) {
return null;
}
if(theme) {
let url = this.urlForComponent(theme);
return url;
return [url];
}
}
postActiveThemeToComponent(component) {
let url = this.getActiveThemeUrl();
if(!url) {
return;
}
/*
@param {object} dialog: {permissions, String, component, callback}
*/
var data = { themes: [url] }
this.sendMessageToComponent(component, {action: "themes", data: data})
presentPermissionsDialog(dialog) {
let text = `${dialog.component.name} would like to interact with your ${dialog.permissionsString}`;
this.alertManager.confirm({
title: "Grant Permissions",
text: text,
confirmButtonText: "Continue",
cancelButtonText: "Cancel",
onConfirm: () => {dialog.callback(true)},
onCancel: () => {dialog.callback(false)},
})
}
postActiveThemesToAllComponents() {
for(var component of this.components) {
// Skip over components that are themes themselves,
// or components that are not active, or components that don't have a window
// On desktop/web, we check if component.active is true as well below, but on mobile,
// .active isn't set
if(component.isTheme() || !component.window) {
continue;
}
this.postActiveThemeToComponent(component);
}
}
/*
Custom functions, not overrides
*/
getEditors() {
return this.componentsForArea("editor-editor");
@@ -416,23 +75,6 @@ export default class ComponentManager {
return this.getEditors().filter((e) => {return e.content.isMobileDefault})[0];
}
editorForNote(note) {
let editors = ModelManager.get().validItemsForContentType("SN|Component").filter(function(component){
return component.area == "editor-editor";
})
for(var editor of editors) {
if(editor.isExplicitlyEnabledForItem(note)) {
return editor;
}
}
// No editor found for note. Use default editor, if note does not prefer system editor
if(!note.content.mobilePrefersPlainEditor) {
return this.getDefaultEditor();
}
}
setEditorAsMobileDefault(editor, isDefault) {
if(isDefault) {
// Remove current default
@@ -443,7 +85,7 @@ export default class ComponentManager {
}
}
// Could be null if plain editor
// Could be null if plain editor
if(editor) {
editor.content.isMobileDefault = isDefault;
editor.setDirty(true);

View File

@@ -2,7 +2,7 @@ import {Platform} from 'react-native';
import * as StoreReview from 'react-native-store-review';
import Storage from "./sfjs/storageManager";
let NumRunsBeforeAskingForReview = [18, 65, 120]
let NumRunsBeforeAskingForReview = [18, 45, 105]
export default class ReviewManager {

View File

@@ -12,6 +12,19 @@ export default class AlertManager extends SFAlertManager {
return this.instance;
}
async alert({title, text, closeButtonText = "OK", onClose} = {}) {
return new Promise((resolve, reject) => {
// On iOS, confirm should go first. On Android, cancel should go first.
let buttons = [
{text: closeButtonText, onPress: () => {
resolve();
onClose && onClose();
}},
];
Alert.alert(title, text, buttons, { cancelable: true })
})
}
async confirm({title, text, confirmButtonText = "OK", cancelButtonText = "Cancel", onConfirm, onCancel} = {}) {
return new Promise((resolve, reject) => {
// On iOS, confirm should go first. On Android, cancel should go first.

View File

@@ -1,7 +1,7 @@
import Storage from "./storageManager"
import "../../models/extend/item.js";
import { SFPredicate, SFPrivileges } from "standard-file-js";
import { SNMfa, SNServerExtension, SNExtension, SNEditor } from 'sn-models';
import { SNMfa, SNServerExtension, SNExtension, SNEditor } from 'snjs';
SFModelManager.ContentTypeClassMapping = {
"Note" : SNNote,
@@ -251,4 +251,25 @@ export default class ModelManager extends SFModelManager {
return {notes: notes, tags: tags};
}
/*
Misc
*/
humanReadableDisplayForContentType(contentType) {
return {
"Note" : "note",
"Tag" : "tag",
"SN|SmartTag": "smart tag",
"Extension" : "action-based extension",
"SN|Component" : "component",
"SN|Editor" : "editor",
"SN|Theme" : "theme",
"SF|Extension" : "server extension",
"SF|MFA" : "two-factor authentication setting",
"SN|FileSafe|Credentials": "FileSafe credential",
"SN|FileSafe|FileMetadata": "FileSafe file",
"SN|FileSafe|Integration": "FileSafe integration"
}[contentType];
}
}

View File

@@ -107,7 +107,9 @@ export default class PrivilegesManager extends SFPrivilegesManager {
let credentials = await this.netCredentialsForAction(action);
let sources = [];
for(var credential of credentials) {
sources = sources.concat(sourcesForCredential(credential));
sources = sources.concat(sourcesForCredential(credential)).sort((a, b) => {
return a.sort - b.sort;
})
}
return sources;

View File

@@ -1,4 +1,6 @@
import { AsyncStorage } from 'react-native';
import { Platform } from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import AlertManager from './alertManager';
export default class Storage extends SFStorageManager {
@@ -12,6 +14,12 @@ export default class Storage extends SFStorageManager {
return this.instance;
}
constructor() {
super();
this.isAndroid = Platform.OS == 'android';
this.platformString = this.isAndroid ? "Android" : "iOS";
}
async getItem(key) {
try {
return AsyncStorage.getItem(key);
@@ -59,25 +67,109 @@ export default class Storage extends SFStorageManager {
// Models
async getAllModels() {
var items = [];
var keys = await AsyncStorage.getAllKeys();
var stores = await AsyncStorage.multiGet(keys);
stores.map((result, i, store) => {
// get at each store's key/value so you can work with it
let key = store[i][0];
if(key.includes("Item-")) {
async getAllModels() {
const itemsFromStores = (stores) => {
let items = [];
stores.map((result, i, store) => {
let key = store[i][0];
let value = store[i][1];
if(value) {
items.push(JSON.parse(value));
}
})
return items;
}
/*
As of react-native-asyncstorage 1.4.0:
If Android has items saved that are over ~1MB, then:
let stores = await AsyncStorage.multiGet(keys);
items = items.concat(itemsFromStores(stores));
will fail silently and no items will load.
let item = await AsyncStorage.getItem(key);
items.push(JSON.parse(item));
will fail with an exception 'Cursor Window: Window is full'.
But actually if you wrap it in a try catch, then it will throw an exception correctly.
So that's what we're using now on Android.
let item = itemsFromStores(await AsyncStorage.multiGet([key]))[0];
items.push(item);
will succeed completely.
Issue created here: https://github.com/react-native-community/react-native-async-storage/issues/105
So what we'll do for now is if Android, use multiGet with just 1 key each time.
We need to determine why multiGet([key]) works, but getItem(key) doesn't.
It looks like the reason getItem fails while multiGet doesn't is because getItem
correctly returns the exception. However, even when there is an exception, getItem
internally still retrieves the item value. So the value and exception are both present,
but only the exception is sent.
Whereas with multiGet, exceptions aren't reported at all, so the value is sent up.
When multiGet gets patched to reports errors, I suspect this loophole will no longer work.
However, getItem's `callback` param can be used instead of the promise, which will return both a value and an exception,
and we can choose which one we want to handle.
multiGet also currently totally fails if even 1 key fails:
https://github.com/react-native-community/react-native-async-storage/issues/106
*/
let keys = await this.getAllModelKeys();
let items = [];
let failedItemIds = [];
if(this.isAndroid) {
for(let key of keys) {
try {
let item = await AsyncStorage.getItem(key);
if(item) {
items.push(JSON.parse(item));
}
} catch (e) {
let id = key.replace("Item-", "");
failedItemIds.push(id);
console.log("Error getting item", key, e);
}
}
})
} else {
try {
let stores = await AsyncStorage.multiGet(keys);
items = items.concat(itemsFromStores(stores));
} catch(e) {
console.log("Error getting items", keys, e);
}
}
if(failedItemIds.length > 0) {
this.showLoadFailForItemIds(failedItemIds);
}
return items;
}
showLoadFailForItemIds(failedItemIds) {
let text = `The following items could not be loaded. This may happen if you are in low-memory conditions, or if the note is very large in size. For compatibility with ${this.platformString}, we recommend breaking up large notes into smaller chunks using the desktop or web app.\n\nItems:\n`
let index = 0;
text += failedItemIds.map((id) => {
let result = id;
if(index != failedItemIds.length - 1) {
result += "\n";
}
index++;
return result;
})
AlertManager.get().alert({title: "Unable to load item", text: text})
}
keyForItem(item) {
return "Item-" + item.uuid;
}

View File

@@ -16,11 +16,16 @@ SFItem.prototype.updateFromJSON = function(json) {
}
}
const original_dateToLocalizedString = SFItem.prototype.dateToLocalizedString;
SFItem.prototype.dateToLocalizedString = function(date) {
return moment(date).format('llll');
}
SFItem.prototype.updatedAtTimestamp = function() {
// date is a moment date
// in the base class we do date.getTime(), but that doesn't work with moment dates.
return this.updated_at.valueOf();
}
// Define these getters
Object.defineProperty(SFItem.prototype, "key", {

View File

@@ -77,6 +77,7 @@ export default class Authenticate extends Abstract {
}
submitPressed() {
// If we just pressed submit on the only pending source left, disable submit button
if(this.pendingSources.length == 1) {
this.setState({submitDisabled: true});
}
@@ -114,13 +115,11 @@ export default class Authenticate extends Abstract {
}
async beginAuthenticationForSource(source) {
if(ApplicationState.get().getMostRecentState() == ApplicationState.LosingFocus) {
// Authentication modal may be displayed on lose focus just before the app is closing.
// In this state however, we don't want to begin auth. We'll wait until the app gains focus.
return;
}
// Authentication modal may be displayed on lose focus just before the app is closing.
// In this state however, we don't want to begin auth. We'll wait until the app gains focus.
let isLosingFocus = ApplicationState.get().getMostRecentState() == ApplicationState.LosingFocus;
if(source.type == "biometric") {
if(source.type == "biometric" && !isLosingFocus) {
// Begin authentication right away, we're not waiting for any input
this.validateAuthentication(source);
} else if(source.type == "input") {
@@ -128,6 +127,7 @@ export default class Authenticate extends Abstract {
source.inputRef.focus();
}
}
source.setWaitingForInput();
this.setState({activeSource: source});
this.forceUpdate();
@@ -160,10 +160,14 @@ export default class Authenticate extends Abstract {
return;
}
// Disable submit while we're processing. Will be re-enabled below.
this.setState({submitDisabled: true});
let result = await source.authenticate();
if(source.isInSuccessState()) {
this.successfulSources.push(source);
_.pull(this.pendingSources, source);
this.forceUpdate();
} else {
if(result.error && result.error.message) {
Alert.alert("Unsuccessful", result.error.message);

View File

@@ -9,6 +9,10 @@ export default class AuthenticationSourceAccountPassword extends AuthenticationS
super();
}
get sort() {
return 1;
}
get identifier() {
return "account-password-auth-source";
}

View File

@@ -18,6 +18,10 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource
})
}
get sort() {
return 2;
}
get identifier() {
return "biometric-auth-source";
}

View File

@@ -13,6 +13,10 @@ export default class AuthenticationSourceLocalPasscode extends AuthenticationSou
});
}
get sort() {
return 0;
}
get identifier() {
return "local-passcode-auth-source";
}

View File

@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Alert, View, WebView, Linking, Platform, Text } from 'react-native';
import { Alert, View, Linking, Platform, Text } from 'react-native';
import { WebView } from 'react-native-webview';
import ComponentManager from '@Lib/componentManager'
import ModelManager from '@Lib/sfjs/modelManager'
@@ -8,7 +9,7 @@ import StyleKit from "@Style/StyleKit"
import ApplicationState from "@Lib/ApplicationState"
import Icon from 'react-native-vector-icons/Ionicons';
export default class Webview extends Component {
export default class ComponentView extends Component {
constructor(props) {
super(props);
@@ -25,12 +26,6 @@ export default class Webview extends Component {
}
});
this.keyboardListener = ApplicationState.get().addEventHandler((event, data) => {
if(event == ApplicationState.KeyboardChangeEvent) {
this.webView.injectJavaScript(`window.scrollTo(0,0); document.body.scrollTop = 0`);
}
});
this.reloadData();
if(!this.note) {
@@ -49,7 +44,7 @@ export default class Webview extends Component {
componentDidMount() {
if(Platform.OS == "android" && Platform.Version <= 23) {
// postMessage doesn't work on Android <= 6 (API version 23) https://github.com/facebook/react-native/issues/11594
Alert.alert('Editors Not Supported', `Your version of Android does not support web editors. Changes you make may not be properly saved. Please switch to the Plain Editor for the best experience.`, [{text: 'OK'}])
Alert.alert('Editors Not Supported', `Web editors require Android 7.0 or greater. Your version does not support web editors. Changes you make may not be properly saved. Please switch to the Plain Editor for the best experience.`, [{text: 'OK'}])
}
}
@@ -71,7 +66,6 @@ export default class Webview extends Component {
componentWillUnmount() {
ComponentManager.get().deregisterHandler(this.identifier);
ComponentManager.get().deactivateComponent(this.editor);
ApplicationState.get().removeEventHandler(this.keyboardListener);
}
onMessage = (message) => {
@@ -80,7 +74,7 @@ export default class Webview extends Component {
return;
}
var data;
let data;
try {
data = JSON.parse(message.nativeEvent.data);
} catch (e) {
@@ -88,14 +82,6 @@ export default class Webview extends Component {
return;
}
// Ignore any incoming events (like save events) if the note is locked. Allow messages that are required for component setup (administrative)
if(this.note.locked && !ComponentManager.get().isReadOnlyMessage(data)) {
if(!this.didShowLockAlert) {
Alert.alert('Note Locked', "This note is locked. Changes you make in the web editor will not be saved. Please unlock this note to make changes.", [{text: 'OK'}])
this.didShowLockAlert = true;
}
return;
}
ComponentManager.get().handleMessage(this.editor, data);
}
@@ -107,6 +93,7 @@ export default class Webview extends Component {
if(this.registrationTimeout) {
clearTimeout(this.registrationTimeout);
}
this.registrationTimeout = setTimeout(() => {
ComponentManager.get().registerComponentWindow(this.editor, this.webView);
}, 1);
@@ -134,6 +121,26 @@ export default class Webview extends Component {
this.props.onLoadError();
}
onShouldStartLoadWithRequest = (request) => {
/*
We want to handle link clicks within an editor by opening the browser instead of loading inline.
On iOS, onShouldStartLoadWithRequest is called for all requests including the initial request to load the editor.
On iOS, clicks in the editors have a navigationType of 'click', but on Android, this is not the case (no navigationType).
However, on Android, this function is not called for the initial request.
So that might be one way to determine if this request is a click or the actual editor load request.
But I don't think it's safe to rely on this being the case in the future.
So on Android, we'll handle url loads only if the url isn't equal to the editor url.
*/
let editorUrl = ComponentManager.get().urlForComponent(this.editor);
if((ApplicationState.isIOS && request.navigationType == 'click')
|| (ApplicationState.isAndroid && request.url != editorUrl)) {
ApplicationState.openURL(request.url);
return false;
}
return true;
}
render() {
var editor = this.editor;
var url = ComponentManager.get().urlForComponent(editor);
@@ -158,12 +165,17 @@ export default class Webview extends Component {
onMessage={this.onMessage}
useWebKit={true}
hideKeyboardAccessoryView={true}
onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
cacheEnabled={true}
automaticallyAdjustContentInsets={false}
contentInset={{top: 0, left: 0, bottom: 0, right: 0}}
scalesPageToFit={true /* Android only, not available with WKWebView */}
injectedJavaScript = {
`window.isNative = "true"`
`(function() {
window.parent.postMessage = function(data) {
window.parent.ReactNativeWebView.postMessage(data);
};
return true;
})()`
}
/>
</View>

View File

@@ -7,7 +7,7 @@ import OptionsState from "@Lib/OptionsState"
import SideMenuManager from "@SideMenu/SideMenuManager"
import Abstract from "./Abstract"
import Webview from "./Webview"
import ComponentView from "./ComponentView"
import ComponentManager from '@Lib/componentManager'
import ApplicationState from "@Lib/ApplicationState"
import LockedView from "@Containers/LockedView";
@@ -351,6 +351,7 @@ export default class Compose extends Abstract {
}
showSavedStatus(success) {
const debouceMs = 300; // minimum time message is shown
if(success) {
if(this.statusTimeout) clearTimeout(this.statusTimeout);
this.statusTimeout = setTimeout(() => {
@@ -361,14 +362,14 @@ export default class Compose extends Abstract {
this.saveError = false;
this.syncTakingTooLong = false;
this.setSubTitle(status);
}, 200)
}, debouceMs)
} else {
if(this.statusTimeout) clearTimeout(this.statusTimeout);
this.statusTimeout = setTimeout(function(){
this.saveError = true;
this.syncTakingTooLong = false;
this.setSubTitle("Sync Unavailable (changes saved offline)", StyleKit.variables.stylekitWarningColor);
}.bind(this), 200)
}.bind(this), debouceMs)
}
}
@@ -403,7 +404,7 @@ export default class Compose extends Abstract {
}
}
this.save();
}, 275)
}, 325);
}
sync(note, callback) {
@@ -510,7 +511,7 @@ export default class Compose extends Abstract {
}
{shouldDisplayEditor &&
<Webview
<ComponentView
key={noteEditor.uuid}
noteId={this.note.uuid}
editorId={noteEditor.uuid}

View File

@@ -26,9 +26,10 @@ export default class NoteCell extends ThemedPureComponent {
_onPressIn = () => {
// Debounce
const delay = 25;
this.selectionTimeout = setTimeout(() => {
this.setState({selected: true});
}, 30);
}, delay);
};
_onPressOut = () => {
@@ -152,6 +153,13 @@ export default class NoteCell extends ThemedPureComponent {
})
}
if(note.content.conflict_of) {
flags.push({
text: "Conflicted Copy",
color: StyleKit.variables.stylekitDangerColor
})
}
return flags;
}
@@ -200,10 +208,6 @@ export default class NoteCell extends ThemedPureComponent {
<Text style={this.styles.deleting}>Deleting...</Text>
}
{note.conflict_of &&
<Text style={this.styles.deleting}>Conflicted Copy</Text>
}
{flags.length > 0 &&
<View style={this.styles.flagsContainer}>
{flags.map((flag) =>

View File

@@ -60,6 +60,7 @@ export default class NoteList extends ThemedComponent {
locked={item.locked /* extraData */}
protected={item.content.protected /* extraData */}
hidePreview={item.content.hidePreview /* extraData */}
conflictOf={item.content.conflict_of /* extraData */}
/>
)
}

View File

@@ -332,7 +332,8 @@ export default class Notes extends Abstract {
_onRefresh() {
this.setSubTitle("Syncing...");
this.setState({refreshing: true});
Sync.get().sync().then(() => {
// Perform integrity checks for hard reloads
Sync.get().sync({performIntegrityCheck: true}).then(() => {
setTimeout(() => {
this.setSubTitle(null);
}, 100);
@@ -353,8 +354,10 @@ export default class Notes extends Abstract {
_onPressItem = (item: hash) => {
var run = () => {
if(item.conflict_of) {
item.conflict_of = null;
if(item.content.conflict_of) {
item.content.conflict_of = null;
item.setDirty(true, true);
Sync.get().sync();
}
this.selectNote(item);

View File

@@ -123,6 +123,7 @@ export default class Root extends Abstract {
componentWillUnmount() {
super.componentWillUnmount();
ApplicationState.get().removeStateObserver(this.stateObserver);
Sync.get().removeEventHandler(this.syncEventHandler);
Sync.get().removeSyncStatusObserver(this.syncStatusObserver);
clearInterval(this.syncTimer);
}
@@ -163,6 +164,9 @@ export default class Root extends Abstract {
}
initializeData() {
// Ensure no sync executes until initial data load
Sync.get().lockSyncing();
let encryptionEnabled = KeysManager.get().isOfflineEncryptionEnabled();
this.setSubTitle(encryptionEnabled ? "Decrypting items..." : "Loading items...");
let incrementalCallback = (current, total) => {
@@ -176,10 +180,12 @@ export default class Root extends Abstract {
}
let loadLocalCompletion = (items) => {
Sync.get().unlockSyncing();
this.setSubTitle("Syncing...");
this.dataLoaded = true;
// perform initial sync
Sync.get().sync().then(() => {
Sync.get().sync({performIntegrityCheck: true}).then(() => {
this.setSubTitle(null);
});
}

View File

@@ -9,6 +9,7 @@ import ActionSheet from 'react-native-actionsheet'
import Abstract from "@Screens/Abstract"
import AlertManager from "@SFJS/alertManager"
import Auth from "@SFJS/authManager"
import Sync from '@SFJS/syncManager'
import SectionHeader from "@Components/SectionHeader";
import TableSection from "@Components/TableSection";
@@ -34,20 +35,41 @@ export default class MainSideMenu extends AbstractSideMenu {
this.signoutObserver = Auth.get().addEventHandler((event) => {
if(event == SFAuthManager.DidSignOutEvent) {
this.setState({outOfSync: false});
this.forceUpdate();
}
});
this.syncEventHandler = Sync.get().addEventHandler((event, data) => {
if(event == "enter-out-of-sync") {
this.setState({outOfSync: true});
} else if(event == "exit-out-of-sync") {
this.setState({outOfSync: false});
}
})
}
componentWillUnmount() {
super.componentWillUnmount();
Auth.get().removeEventHandler(this.signoutObserver);
Sync.get().removeEventHandler(this.syncEventHandler);
}
presentSettings() {
this.props.navigation.navigate("Settings");
}
outOfSyncPressed() {
AlertManager.get().confirm({
title: "Potentially Out of Sync",
text: "We've detected that the data in the current application session may not match the data on the server. This can happen due to poor network conditions, or if a large note fails to download on your device. To resolve this issue, we recommend first creating a backup of your data in the Settings screen, the signing out of your account and signing back in.",
confirmButtonText: "Open Settings",
onConfirm: () => {
this.presentSettings();
}
})
}
get handler() {
return SideMenuManager.get().getHandlerForLeftSideMenu();
}
@@ -151,7 +173,11 @@ export default class MainSideMenu extends AbstractSideMenu {
<SafeAreaView style={this.styles.firstSafeArea} />
<SafeAreaView style={[viewStyles, this.styles.secondSafeArea]}>
<SideMenuHero onPress={() => {this.presentSettings()}}/>
<SideMenuHero
outOfSync={this.state.outOfSync}
onPress={() => {this.presentSettings()}}
onOutOfSyncPress={() => {this.outOfSyncPressed()}}
/>
<ScrollView style={this.styles.scrollView} removeClippedSubviews={false}>

View File

@@ -7,6 +7,7 @@ import ModelManager from "@SFJS/modelManager"
import StyleKit from "@Style/StyleKit"
import ThemedComponent from "@Components/ThemedComponent";
import Circle from "@Components/Circle"
export default class SideMenuHero extends ThemedComponent {
@@ -43,6 +44,15 @@ export default class SideMenuHero extends ThemedComponent {
<TouchableOpacity onPress={this.props.onPress}>
<Text style={this.styles.subtitle}>{textData.text}</Text>
</TouchableOpacity>
{this.props.outOfSync &&
<TouchableOpacity style={this.styles.outOfSyncContainer} onPress={this.props.onOutOfSyncPress}>
<View style={this.styles.iconCircle}>
<Circle size={10} backgroundColor={StyleKit.variables.stylekitWarningColor} borderColor={StyleKit.variables.stylekitWarningColor} />
</View>
<Text style={this.styles.outOfSync}>Potentially Out of Sync</Text>
</TouchableOpacity>
}
</View>
)
}
@@ -70,6 +80,24 @@ export default class SideMenuHero extends ThemedComponent {
fontSize: 13,
color: StyleKit.variables.stylekitContrastForegroundColor,
opacity: 0.6
},
outOfSyncContainer: {
flex: 0,
flexDirection: 'row',
alignItems: "center"
},
iconCircle: {
marginTop: 10,
width: 15,
},
outOfSync: {
marginTop: 10,
fontSize: 13,
color: StyleKit.variables.stylekitWarningColor,
fontWeight: "bold"
}
}
}