From 7cd6ed0ff808f8d07a4eae40a0a61be122711fc2 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 25 Sep 2017 12:45:35 -0500 Subject: [PATCH] SNTextView for iOS --- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 4 +- ios/StandardNotes.xcodeproj/project.pbxproj | 55 ++-- ios/StandardNotes/AppDelegate.m | 1 + package.json | 2 +- src/Styles.js | 30 +- src/app.js | 33 +- src/components/ButtonCell.js | 2 +- src/screens/Abstract.js | 6 +- src/screens/Account.js | 16 +- src/screens/Compose.js | 112 ++----- .../standardnotes/sntextview/SNTextView.java | 58 ++-- .../sntextview/SNTextViewManager.java | 4 +- .../ios/SNTextView.xcodeproj/project.pbxproj | 289 ++++++++++++++++++ .../sn-textview/ios/SNTextView/SNTextView.h | 23 ++ .../sn-textview/ios/SNTextView/SNTextView.m | 88 ++++++ .../ios/SNTextView/SNTextViewManager.h | 13 + .../ios/SNTextView/SNTextViewManager.m | 61 ++++ vendor/sn-textview/src/TextView.js | 32 +- 19 files changed, 677 insertions(+), 156 deletions(-) create mode 100644 vendor/sn-textview/ios/SNTextView.xcodeproj/project.pbxproj create mode 100644 vendor/sn-textview/ios/SNTextView/SNTextView.h create mode 100644 vendor/sn-textview/ios/SNTextView/SNTextView.m create mode 100644 vendor/sn-textview/ios/SNTextView/SNTextViewManager.h create mode 100644 vendor/sn-textview/ios/SNTextView/SNTextViewManager.m diff --git a/android/app/build.gradle b/android/app/build.gradle index 2134e2b3..7a599e9a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -97,8 +97,8 @@ android { applicationId "com.standardnotes" minSdkVersion 19 targetSdkVersion 25 - versionCode 3 - versionName "0.0.3" + versionCode 4 + versionName "0.0.4" multiDexEnabled true ndk { abiFilters "armeabi-v7a", "x86" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6d6372bd..8e6234cc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,6 @@ + package="com.standardnotes"> diff --git a/ios/StandardNotes.xcodeproj/project.pbxproj b/ios/StandardNotes.xcodeproj/project.pbxproj index 3a018ccf..d499663c 100644 --- a/ios/StandardNotes.xcodeproj/project.pbxproj +++ b/ios/StandardNotes.xcodeproj/project.pbxproj @@ -5,6 +5,7 @@ }; 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 */; }; @@ -12,7 +13,6 @@ 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00E356F31AD99517003FC87E /* StandardNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StandardNotesTests.m */; }; - 0A0CB2FEBD4F4E26904E2C1A /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0752A2CAF82B4C9198CAC803 /* SimpleLineIcons.ttf */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; @@ -34,26 +34,18 @@ 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; 2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; 2DCD954D1E0B4F2C00145EB5 /* StandardNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StandardNotesTests.m */; }; - 308F6AF684F14FBE8906A535 /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 13BC74D00F074F59BBADD45D /* Entypo.ttf */; }; - 3F3B0F19ED8C44CB9F287156 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6314B32C63AC4827A9329AA6 /* MaterialCommunityIcons.ttf */; }; 40F325481D4F4F04AC15D0A9 /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 48127930FB1344778C168838 /* libRNVectorIcons.a */; }; - 4458D6CB9D184CE586F6B281 /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D38724A93AC141D6B51D1356 /* Foundation.ttf */; }; - 4A03A911D0084A9F94552332 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 147893EEE8924AA4AE56F3D7 /* MaterialIcons.ttf */; }; 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 965031D980094619B7DBA0FD /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1F569402A90047A59845394A /* Ionicons.ttf */; }; 9A2C235D0ABA4B0CB9A428CA /* libRNKeychain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDCF33ADCFE845D588CC4E66 /* libRNKeychain.a */; }; ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; }; - B299548F9AC848C0A84B3AF8 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 12E89137A7104D91898C2734 /* FontAwesome.ttf */; }; - B3A5C7D4B7AE403EA161544F /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 00457F9447544666906F6C53 /* Zocial.ttf */; }; - B5F4669B5B7E45A5BF742774 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 59DCF8530F2945FFAA0300FD /* Octicons.ttf */; }; C00E89E3E7F949A4AA0F613E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F30913DACE34E71895FBF91 /* libz.tbd */; }; + CD17667C1F795DC100165C83 /* libSNTextView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD1766781F795AE500165C83 /* libSNTextView.a */; }; CDB58A1F1F6C518E009EF868 /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A1E1F6C5182009EF868 /* libReactNativeNavigation.a */; }; CDB58A201F6C5193009EF868 /* libRNMail.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A161F6C5179009EF868 /* libRNMail.a */; }; CDB58A211F6C51A4009EF868 /* libReactNativeFingerprintScanner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A0F1F6C5174009EF868 /* libReactNativeFingerprintScanner.a */; }; CDB58A221F6C5235009EF868 /* libRCTAes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB58A091F6C516B009EF868 /* libRCTAes.a */; }; - DF947D9402FF426FADD85603 /* Feather.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2416263A135F439AA3C5F9D2 /* Feather.ttf */; }; - E2AF9A1843594A7D9214736B /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B5409EE82FED4DA9B57CA746 /* EvilIcons.ttf */; }; F0D84FDB75374348BC0017C2 /* libBugsnagReactNative.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFD3F9197A5F41C0904D7E60 /* libBugsnagReactNative.a */; }; /* End PBXBuildFile section */ @@ -254,6 +246,13 @@ remoteGlobalIDString = 358F4ED71D1E81A9004DF814; remoteInfo = RCTBlob; }; + CD1766771F795AE500165C83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CD17664C1F795AE500165C83 /* SNTextView.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = CD2830C11F795AB7002B9529; + remoteInfo = SNTextView; + }; CD885F771F70228400B91C20 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 737AF67874434967865855D8 /* BugsnagReactNative.xcodeproj */; @@ -388,6 +387,7 @@ 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = ""; }; B5409EE82FED4DA9B57CA746 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; + CD17664C1F795AE500165C83 /* SNTextView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SNTextView.xcodeproj; path = "../vendor/sn-textview/ios/SNTextView.xcodeproj"; sourceTree = ""; }; CDB58A041F6C516B009EF868 /* RCTAes.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAes.xcodeproj; path = "../vendor/react-native-aes/ios/RCTAes.xcodeproj"; sourceTree = ""; }; CDB58A0A1F6C5174009EF868 /* ReactNativeFingerprintScanner.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeFingerprintScanner.xcodeproj; path = "../vendor/react-native-fingerprint-scanner/ios/ReactNativeFingerprintScanner.xcodeproj"; sourceTree = ""; }; CDB58A101F6C5178009EF868 /* RNMail.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNMail.xcodeproj; path = "../vendor/react-native-mail/RNMail.xcodeproj"; sourceTree = ""; }; @@ -411,6 +411,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CD17667C1F795DC100165C83 /* libSNTextView.a in Frameworks */, CDB58A221F6C5235009EF868 /* libRCTAes.a in Frameworks */, CDB58A211F6C51A4009EF868 /* libReactNativeFingerprintScanner.a in Frameworks */, CDB58A201F6C5193009EF868 /* libRNMail.a in Frameworks */, @@ -610,6 +611,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + CD17664C1F795AE500165C83 /* SNTextView.xcodeproj */, CDB58A191F6C5182009EF868 /* ReactNativeNavigation.xcodeproj */, CDB58A101F6C5178009EF868 /* RNMail.xcodeproj */, CDB58A0A1F6C5174009EF868 /* ReactNativeFingerprintScanner.xcodeproj */, @@ -678,6 +680,14 @@ name = Products; sourceTree = ""; }; + CD17664D1F795AE500165C83 /* Products */ = { + isa = PBXGroup; + children = ( + CD1766781F795AE500165C83 /* libSNTextView.a */, + ); + name = Products; + sourceTree = ""; + }; CD885F741F70228400B91C20 /* Products */ = { isa = PBXGroup; children = ( @@ -952,6 +962,10 @@ ProductGroup = CDB58A641F6C5294009EF868 /* Products */; ProjectRef = 54ED130E749A46A3B15B27F2 /* RNVectorIcons.xcodeproj */; }, + { + ProductGroup = CD17664D1F795AE500165C83 /* Products */; + ProjectRef = CD17664C1F795AE500165C83 /* SNTextView.xcodeproj */; + }, ); projectRoot = ""; targets = ( @@ -1146,6 +1160,13 @@ remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + CD1766781F795AE500165C83 /* libSNTextView.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libSNTextView.a; + remoteRef = CD1766771F795AE500165C83 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; CD885F781F70228400B91C20 /* libBugsnagReactNative.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1253,17 +1274,7 @@ files = ( 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - 308F6AF684F14FBE8906A535 /* Entypo.ttf in Resources */, - E2AF9A1843594A7D9214736B /* EvilIcons.ttf in Resources */, - B299548F9AC848C0A84B3AF8 /* FontAwesome.ttf in Resources */, - 4458D6CB9D184CE586F6B281 /* Foundation.ttf in Resources */, 965031D980094619B7DBA0FD /* Ionicons.ttf in Resources */, - 3F3B0F19ED8C44CB9F287156 /* MaterialCommunityIcons.ttf in Resources */, - 4A03A911D0084A9F94552332 /* MaterialIcons.ttf in Resources */, - B5F4669B5B7E45A5BF742774 /* Octicons.ttf in Resources */, - 0A0CB2FEBD4F4E26904E2C1A /* SimpleLineIcons.ttf in Resources */, - B3A5C7D4B7AE403EA161544F /* Zocial.ttf in Resources */, - DF947D9402FF426FADD85603 /* Feather.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1455,10 +1466,12 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../vendor/react-native-navigation/ios", "$(SRCROOT)/../node_modules/bugsnag-react-native/cocoa/**", + "$(SRCROOT)/../node_modules/react-native/Libraries/Text", ); INFOPLIST_FILE = StandardNotes/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ""; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1484,10 +1497,12 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../vendor/react-native-navigation/ios", "$(SRCROOT)/../node_modules/bugsnag-react-native/cocoa/**", + "$(SRCROOT)/../node_modules/react-native/Libraries/Text", ); INFOPLIST_FILE = StandardNotes/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ""; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/ios/StandardNotes/AppDelegate.m b/ios/StandardNotes/AppDelegate.m index 74ed0950..a366c0aa 100644 --- a/ios/StandardNotes/AppDelegate.m +++ b/ios/StandardNotes/AppDelegate.m @@ -13,6 +13,7 @@ #import "RCCManager.h" #import #import +#import "RCTTextView.h" @implementation AppDelegate diff --git a/package.json b/package.json index 7795b7ca..6c3f9586 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "immutable": "^3.8.1", "lodash": "^4.17.4", "react": "16.0.0-beta.5", - "react-native": "^0.48.3", + "react-native": "^0.48.4", "react-native-keychain": "^1.2.1", "react-native-search-box": "0.0.11", "react-native-vector-icons": "^4.3.0" diff --git a/src/Styles.js b/src/Styles.js index 0cbc0586..7e0ae419 100644 --- a/src/Styles.js +++ b/src/Styles.js @@ -38,7 +38,7 @@ export default class GlobalStyles { theme.active = true; this.activeTheme = theme; var constants = this.defaultConstants(); - this.setStyles(this.defaultRules(constants), constants, constants.statusBar); + this.setStyles(this.defaultRules(constants), constants, theme.mobileRules.statusBar); } }.bind(this)); } @@ -224,7 +224,7 @@ export default class GlobalStyles { mainBackgroundColor: "#ffffff", mainTintColor: "#fb0206", mainDimColor: "#9d9d9d", - mainTextColor: "black", + mainTextColor: "#000000", mainTextFontSize: 16, mainHeaderFontSize: 16, @@ -318,9 +318,9 @@ export default class GlobalStyles { }, sectionedAccessoryTableCellLabel: { - paddingTop: 10, fontSize: constants.mainTextFontSize, color: constants.mainTextColor, + paddingTop: 12, }, buttonCell: { @@ -337,7 +337,11 @@ export default class GlobalStyles { color: Platform.OS == "android" ? constants.mainTextColor : constants.mainTintColor, fontSize: constants.mainTextFontSize, height: "100%", - paddingTop: 10, + paddingTop: 13, + }, + + buttonCellButtonAndroid: { + paddingTop: 11 }, buttonCellButtonLeft: { @@ -345,6 +349,24 @@ export default class GlobalStyles { paddingLeft: constants.paddingLeft }, + noteText: { + flexGrow: 1, + fontSize: 17, + marginTop: 0, + paddingTop: 10, + color: constants.mainTextColor, + paddingLeft: constants.paddingLeft, + paddingRight: constants.paddingLeft, + paddingBottom: 10, + // textAlignVertical: 'top', + // lineHeight: 22, + }, + + noteTextIOS: { + paddingLeft: constants.paddingLeft - 5, + paddingRight: constants.paddingLeft - 5, + }, + bold: { fontWeight: "bold" }, diff --git a/src/app.js b/src/app.js index 72de624a..d6a21422 100644 --- a/src/app.js +++ b/src/app.js @@ -110,6 +110,10 @@ export default class App { return this.get().isAndroid; } + static get isIOS() { + return this.get().isIOS; + } + get isAndroid() { return this._isAndroid; } @@ -310,20 +314,6 @@ export default class App { // recursion this.isStartingApp = true; - let drawer = { - left: { - screen: 'sn.Filter', - passProps: { - singleSelectMode: true, - options: JSON.stringify(this.optionsState), - onOptionsChange: (options) => { - this.optionsState.mergeWith(options); - } - } - }, - disableOpenGesture: false - }; - if(this.isIOS) { let tabs = [{ label: 'Notes', @@ -343,11 +333,24 @@ export default class App { animationType: this.isIOS ? 'slide-down' : 'fade', tabsStyle: _.clone(this.tabStyles), // for iOS appStyle: _.clone(this.tabStyles), // for Android - drawer: drawer, animationType: 'none' } ); } else { + let drawer = { + left: { + screen: 'sn.Filter', + passProps: { + singleSelectMode: true, + options: JSON.stringify(this.optionsState), + onOptionsChange: (options) => { + this.optionsState.mergeWith(options); + } + } + }, + disableOpenGesture: false + }; + Navigation.startSingleScreenApp( { screen: { diff --git a/src/components/ButtonCell.js b/src/components/ButtonCell.js index 89c1297a..1a5848fb 100644 --- a/src/components/ButtonCell.js +++ b/src/components/ButtonCell.js @@ -6,7 +6,7 @@ import GlobalStyles from "../Styles" export default class ButtonCell extends Component { rules() { - var rules = [GlobalStyles.styles().buttonCellButton]; + var rules = [GlobalStyles.stylesForKey("buttonCellButton")]; if(this.props.leftAligned) { rules.push(GlobalStyles.styles().buttonCellButtonLeft) } if(this.props.bold) { rules.push(GlobalStyles.styles().bold) } if(this.props.disabled) { rules.push({color: "gray", opacity: 0.6}) } diff --git a/src/screens/Abstract.js b/src/screens/Abstract.js index 0cf596f9..edad347b 100644 --- a/src/screens/Abstract.js +++ b/src/screens/Abstract.js @@ -61,6 +61,10 @@ export default class Abstract extends Component { this.props.navigator.dismissModal({animationType: "slide-down"}) } + viewDidAppear() { + this.visible = true; + } + onNavigatorEvent(event) { switch(event.id) { @@ -69,7 +73,7 @@ export default class Abstract extends Component { this.configureNavBar(false); break; case 'didAppear': - this.visible = true; + this.viewDidAppear(); break; case 'willDisappear': this.willBeVisible = false; diff --git a/src/screens/Account.js b/src/screens/Account.js index 7a41e642..eb89b138 100644 --- a/src/screens/Account.js +++ b/src/screens/Account.js @@ -104,7 +104,7 @@ export default class Account extends Abstract { if (event.type == 'NavBarButtonPress') { if (event.id == 'cancel') { - this.dismissModal(); + this.returnToNotesScreen(); } } } @@ -152,6 +152,7 @@ export default class Account extends Abstract { onRegisterPress = (params, callback) => { Keyboard.dismiss(); + var email = params.email; var password = params.password; @@ -197,7 +198,18 @@ export default class Account extends Abstract { onAuthSuccess = () => { this.markAllDataDirtyAndSync(); - this.dismissModal(); + this.returnToNotesScreen(); + } + + returnToNotesScreen = () => { + if(App.isIOS) { + this.props.navigator.switchToTab({ + tabIndex: 0 + }); + this.forceUpdate(); + } else { + this.dismissModal(); + } } onSignOutPress = () => { diff --git a/src/screens/Compose.js b/src/screens/Compose.js index 004eeaf9..40f87a0e 100644 --- a/src/screens/Compose.js +++ b/src/screens/Compose.js @@ -5,23 +5,18 @@ import ModelManager from '../lib/modelManager' import Note from '../models/app/note' import Abstract from "./Abstract" import Icons from '../Icons'; -var dismissKeyboard = require('dismissKeyboard'); +import App from '../app' var _ = require('lodash'); import TextView from "sn-textview"; import { - AppRegistry, StyleSheet, TextInput, View, - FlatList, - TouchableHighlight, - ScrollView, - Text, - Keyboard, KeyboardAvoidingView, - Platform + Platform, + Keyboard } from 'react-native'; import GlobalStyles from "../Styles" @@ -34,50 +29,44 @@ export default class Compose extends Abstract { constructor(props) { super(props); - this.state = {}; - var note = ModelManager.getInstance().findItem(this.props.noteId); + var note = ModelManager.getInstance().findItem(props.noteId); if(!note) { note = new Note({}); note.dummy = true; } this.note = note; + this.state = {title: note.title, text: note.text}; this.loadStyles(); this.syncObserver = Sync.getInstance().registerSyncObserver((changesMade, retreived, saved) => { if(retreived && this.note.uuid && retreived.map((i) => i.uuid).includes(this.note.uuid)) { - this.forceUpdate(); + this.mergeState({title: this.note.title, text: this.note.text}); } }); } - onContentSizeChange = (c) => { - // This function must not be deleted. This is called by TextInput on onContentSizeChange - // It for some reason makes it so that TextInput starts at the top and not the bottom for a long note - } - componentWillMount () { super.componentWillMount(); - this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow); - this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide); - } - - _keyboardDidShow = () => { - this.mergeState({keyboard: true}) - } - - _keyboardDidHide = () => { - this.mergeState({keyboard: false}) } componentWillUnmount() { super.componentWillUnmount(); - this.keyboardDidShowListener.remove(); - this.keyboardDidHideListener.remove(); Sync.getInstance().removeSyncObserver(this.syncObserver); } + viewDidAppear() { + super.viewDidAppear(); + + // Autofocus doesn't work properly on iOS due to navigation push, so we'll focus manually + if(App.isIOS) { + if(this.note.dummy) { + this.input.focus(); + } + } + } + configureNavBar(initial) { super.configureNavBar(); @@ -90,7 +79,6 @@ export default class Compose extends Abstract { title: "Manage", id: 'tags', showAsAction: 'ifRoom', - // buttonColor: GlobalStyles.constants().mainTintColor, } if(Platform.OS === "android") { @@ -128,6 +116,7 @@ export default class Compose extends Abstract { } showOptions() { + this.input.blur(); this.previousOptions = {selectedTags: this.note.tags.map(function(tag){return tag.uuid})}; this.props.navigator.push({ @@ -236,37 +225,24 @@ export default class Compose extends Abstract { subtitle: title }); + var color = GlobalStyles.constantForKey(App.isIOS ? "mainTextColor" : "navBarTextColor"); this.props.navigator.setStyle({ - navBarSubtitleColor: GlobalStyles.hexToRGBA(GlobalStyles.constantForKey("navBarTextColor"), 0.5), + navBarSubtitleColor: GlobalStyles.hexToRGBA(color, 0.5), navBarSubtitleFontSize: 12 }); } - onTextFocus = () => { - // in order to call blur() later, we need to focus here manually, even though it does nothing - // this.refs.input.focus(); - this.isFocused = true; - } - - onTextBlur = () => { - this.isFocused = false; - } - render() { if(this.state.lockContent) { return (); } - var textBottomPadding = 10; - var keyboardBehavior = Platform.OS == "android" ? "height" : "padding"; - var keyboardOffset = this.rawStyles.noteTitle.height + this.rawStyles.noteText.paddingTop + (Platform.OS == "android" ? 15 : 0); - return ( - this.input = ref} autoFocus={this.note.dummy} - text={this.note.text} + value={this.note.text} selectionColor={GlobalStyles.lighten(GlobalStyles.constants().mainTintColor)} onChangeText={this.onTextChange} /> @@ -286,27 +262,16 @@ export default class Compose extends Abstract { } {Platform.OS == "ios" && - - {}} - onContentSizeChange={this.onContentSizeChange} - autoCapitalize={'sentences'} - /> - + this.input = ref} + autoFocus={false} + value={this.note.text} + keyboardDismissMode={'interactive'} + selectionColor={GlobalStyles.lighten(GlobalStyles.constants().mainTintColor)} + onChangeText={this.onTextChange} + /> + } ); @@ -343,19 +308,8 @@ export default class Compose extends Abstract { noteTextContainer: { flexGrow: 1, + flex: 1, }, - - noteText: { - flexGrow: 1, - // fontSize: 17, - marginTop: 0, - paddingTop: 10, - color: GlobalStyles.constants().mainTextColor, - paddingLeft: GlobalStyles.constants().paddingLeft, - paddingRight: GlobalStyles.constants().paddingLeft, - // textAlignVertical: 'top', - // lineHeight: 22, - } } this.styles = StyleSheet.create(this.rawStyles); diff --git a/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextView.java b/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextView.java index fc216912..e90b5704 100644 --- a/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextView.java +++ b/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextView.java @@ -3,17 +3,12 @@ package com.standardnotes.sntextview; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; -import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.support.v4.content.ContextCompat; import android.text.Editable; -import android.text.Layout; import android.text.TextWatcher; -import android.util.Log; import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.LinearLayout; @@ -26,12 +21,13 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.Spacing; +import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.facebook.react.views.textinput.ReactEditText; import java.lang.reflect.Field; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static java.security.AccessController.getContext; /** * Created by mo on 9/20/17. @@ -41,7 +37,8 @@ public class SNTextView extends LinearLayout { private EditText editText; private ScrollView scrollView; - private Boolean ignoreNextTextEvent = false; + private Boolean ignoreNextLocalTextChange = false; + private Boolean ignoreNextIncomingTextChange = false; @SuppressLint("ResourceAsColor") public SNTextView(Context context) { @@ -58,19 +55,46 @@ public class SNTextView extends LinearLayout { editText.setGravity(Gravity.TOP); editText.addTextChangedListener(new TextWatcher() { + private EventDispatcher mEventDispatcher; + private ReactEditText mEditText; + private String mPreviousText; + @Override public void afterTextChanged(Editable s) {} @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + mPreviousText = s.toString(); + } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - if(ignoreNextTextEvent) { - ignoreNextTextEvent = false; + // Rearranging the text (i.e. changing between singleline and multiline attributes) can + // also trigger onTextChanged, call the event in JS only when the text actually changed + if (count == 0 && before == 0) { return; } - textDidChange(s.toString()); + + + String newText = s.toString().substring(start, start + count); + String oldText = mPreviousText.substring(start, start + before); + // Don't send same text changes + if (count == before && newText.equals(oldText)) { + return; + } + + if(ignoreNextLocalTextChange) { + ignoreNextLocalTextChange = false; + return; + } + + WritableMap event = Arguments.createMap(); + event.putString("text", s.toString()); + + final Context context = getContext(); + if (context instanceof ReactContext) { + ((ReactContext) context).getJSModule(RCTEventEmitter.class).receiveEvent(getId(),"onChangeText", event); + } } }); @@ -83,9 +107,8 @@ public class SNTextView extends LinearLayout { super.onLayout(changed, left, top, right, bottom); } - public void setText(String text) { - ignoreNextTextEvent = true; + ignoreNextLocalTextChange = true; editText.setText(text); } @@ -153,13 +176,4 @@ public class SNTextView extends LinearLayout { } } - public void textDidChange(String text) { - WritableMap event = Arguments.createMap(); - event.putString("message", editText.getText().toString()); - - final Context context = getContext(); - if (context instanceof ReactContext) { - ((ReactContext) context).getJSModule(RCTEventEmitter.class).receiveEvent(getId(),"onChangeTextValue", event); - } - } } diff --git a/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextViewManager.java b/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextViewManager.java index 9578fd86..b574fc97 100644 --- a/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextViewManager.java +++ b/vendor/sn-textview/android/src/main/java/com/standardnotes/sntextview/SNTextViewManager.java @@ -117,8 +117,8 @@ public class SNTextViewManager extends SimpleViewManager { @Override public @Nullable Map getExportedCustomDirectEventTypeConstants() { return MapBuilder.of( - "onChangeTextValue", - MapBuilder.of("registrationName", "onChangeTextValue") + "onChangeText", + MapBuilder.of("registrationName", "onChangeText") ); } diff --git a/vendor/sn-textview/ios/SNTextView.xcodeproj/project.pbxproj b/vendor/sn-textview/ios/SNTextView.xcodeproj/project.pbxproj new file mode 100644 index 00000000..c016d281 --- /dev/null +++ b/vendor/sn-textview/ios/SNTextView.xcodeproj/project.pbxproj @@ -0,0 +1,289 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + CD17667B1F795B1C00165C83 /* SNTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = CD17667A1F795B1C00165C83 /* SNTextViewManager.m */; }; + CD2830C61F795AB7002B9529 /* SNTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = CD2830C51F795AB7002B9529 /* SNTextView.m */; }; + CD2830C71F795AB7002B9529 /* SNTextView.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CD2830C41F795AB7002B9529 /* SNTextView.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + CD2830BF1F795AB7002B9529 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + CD2830C71F795AB7002B9529 /* SNTextView.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + CD1766791F795B1C00165C83 /* SNTextViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTextViewManager.h; sourceTree = ""; }; + CD17667A1F795B1C00165C83 /* SNTextViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTextViewManager.m; sourceTree = ""; }; + CD2830C11F795AB7002B9529 /* libSNTextView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSNTextView.a; sourceTree = BUILT_PRODUCTS_DIR; }; + CD2830C41F795AB7002B9529 /* SNTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTextView.h; sourceTree = ""; }; + CD2830C51F795AB7002B9529 /* SNTextView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTextView.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CD2830BE1F795AB7002B9529 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CD2830B81F795AB7002B9529 = { + isa = PBXGroup; + children = ( + CD2830C31F795AB7002B9529 /* SNTextView */, + CD2830C21F795AB7002B9529 /* Products */, + ); + sourceTree = ""; + }; + CD2830C21F795AB7002B9529 /* Products */ = { + isa = PBXGroup; + children = ( + CD2830C11F795AB7002B9529 /* libSNTextView.a */, + ); + name = Products; + sourceTree = ""; + }; + CD2830C31F795AB7002B9529 /* SNTextView */ = { + isa = PBXGroup; + children = ( + CD2830C41F795AB7002B9529 /* SNTextView.h */, + CD2830C51F795AB7002B9529 /* SNTextView.m */, + CD1766791F795B1C00165C83 /* SNTextViewManager.h */, + CD17667A1F795B1C00165C83 /* SNTextViewManager.m */, + ); + path = SNTextView; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CD2830C01F795AB7002B9529 /* SNTextView */ = { + isa = PBXNativeTarget; + buildConfigurationList = CD2830CA1F795AB7002B9529 /* Build configuration list for PBXNativeTarget "SNTextView" */; + buildPhases = ( + CD2830BD1F795AB7002B9529 /* Sources */, + CD2830BE1F795AB7002B9529 /* Frameworks */, + CD2830BF1F795AB7002B9529 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SNTextView; + productName = SNTextView; + productReference = CD2830C11F795AB7002B9529 /* libSNTextView.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CD2830B91F795AB7002B9529 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = standardnotes; + TargetAttributes = { + CD2830C01F795AB7002B9529 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = CD2830BC1F795AB7002B9529 /* Build configuration list for PBXProject "SNTextView" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = CD2830B81F795AB7002B9529; + productRefGroup = CD2830C21F795AB7002B9529 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CD2830C01F795AB7002B9529 /* SNTextView */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + CD2830BD1F795AB7002B9529 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CD17667B1F795B1C00165C83 /* SNTextViewManager.m in Sources */, + CD2830C61F795AB7002B9529 /* SNTextView.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CD2830C81F795AB7002B9529 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + 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 = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + CD2830C91F795AB7002B9529 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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 = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CD2830CB1F795AB7002B9529 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CD2830CC1F795AB7002B9529 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CD2830BC1F795AB7002B9529 /* Build configuration list for PBXProject "SNTextView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CD2830C81F795AB7002B9529 /* Debug */, + CD2830C91F795AB7002B9529 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CD2830CA1F795AB7002B9529 /* Build configuration list for PBXNativeTarget "SNTextView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CD2830CB1F795AB7002B9529 /* Debug */, + CD2830CC1F795AB7002B9529 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CD2830B91F795AB7002B9529 /* Project object */; +} diff --git a/vendor/sn-textview/ios/SNTextView/SNTextView.h b/vendor/sn-textview/ios/SNTextView/SNTextView.h new file mode 100644 index 00000000..3f8fbb0e --- /dev/null +++ b/vendor/sn-textview/ios/SNTextView/SNTextView.h @@ -0,0 +1,23 @@ +// +// SNTextView.h +// SNTextView +// +// Created by mo on 9/25/17. +// Copyright © 2017 standardnotes. All rights reserved. +// + +#import +#import +#import + +@interface SNTextView : UITextView + +@property (nonatomic, copy) RCTBubblingEventBlock onChangeText; +@property (nonatomic, assign) UIScrollViewKeyboardDismissMode keyboardDismissMode; + +@property (nonatomic, assign) CGFloat paddingTop; +@property (nonatomic, assign) CGFloat paddingLeft; +@property (nonatomic, assign) CGFloat paddingRight; +@property (nonatomic, assign) CGFloat paddingBottom; + +@end diff --git a/vendor/sn-textview/ios/SNTextView/SNTextView.m b/vendor/sn-textview/ios/SNTextView/SNTextView.m new file mode 100644 index 00000000..0219bd50 --- /dev/null +++ b/vendor/sn-textview/ios/SNTextView/SNTextView.m @@ -0,0 +1,88 @@ +// +// SNTextView.m +// SNTextView +// +// Created by mo on 9/25/17. +// Copyright © 2017 standardnotes. All rights reserved. +// + +#import "SNTextView.h" + +@implementation SNTextView +{ + BOOL didLayout; +} + +- (instancetype)init { + if(self = [super init]) { + + __weak typeof(self) weakself = self; + + self.contentInset = UIEdgeInsetsZero; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardDidShowNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + CGSize keyboardSize = [[[note userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0); + weakself.contentInset = insets; + weakself.scrollIndicatorInsets = insets; + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillHideNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + UIEdgeInsets insets = UIEdgeInsetsZero; + weakself.contentInset = insets; + weakself.scrollIndicatorInsets = insets; + }]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + // There's an issue where a large blob of text without paragraphs will start at the bottom. + // This makes it go up to the top + if(!didLayout) { + didLayout = true; + [self setContentOffset:CGPointZero]; + } +} + +- (void)setPaddingTop:(CGFloat)paddingTop +{ + _paddingTop = paddingTop; + + UIEdgeInsets insets = self.textContainerInset; + [self setPaddingTop:paddingTop left:insets.left bottom:insets.bottom right:insets.right]; +} + +- (void)setPaddingLeft:(CGFloat)paddingLeft +{ + _paddingLeft = paddingLeft; + + UIEdgeInsets insets = self.textContainerInset; + [self setPaddingTop:insets.top left:paddingLeft bottom:insets.bottom right:insets.right]; +} + +- (void)setPaddingBottom:(CGFloat)paddingBottom +{ + _paddingBottom = paddingBottom; + + UIEdgeInsets insets = self.textContainerInset; + [self setPaddingTop:insets.top left:insets.left bottom:paddingBottom right:insets.right]; +} + +- (void)setPaddingRight:(CGFloat)paddingRight +{ + _paddingRight = paddingRight; + + UIEdgeInsets insets = self.textContainerInset; + [self setPaddingTop:insets.top left:insets.left bottom:insets.bottom right:paddingRight]; +} + +- (void)setPaddingTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right +{ + UIEdgeInsets insets = UIEdgeInsetsMake(top, left, bottom, right); + self.textContainerInset = insets; +} + +@end diff --git a/vendor/sn-textview/ios/SNTextView/SNTextViewManager.h b/vendor/sn-textview/ios/SNTextView/SNTextViewManager.h new file mode 100644 index 00000000..d75ced1e --- /dev/null +++ b/vendor/sn-textview/ios/SNTextView/SNTextViewManager.h @@ -0,0 +1,13 @@ +// +// SNTextViewManager.h +// SNTextView +// +// Created by mo on 9/25/17. +// Copyright © 2017 standardnotes. All rights reserved. +// + +#import + +@interface SNTextViewManager : RCTViewManager + +@end diff --git a/vendor/sn-textview/ios/SNTextView/SNTextViewManager.m b/vendor/sn-textview/ios/SNTextView/SNTextViewManager.m new file mode 100644 index 00000000..aa01210a --- /dev/null +++ b/vendor/sn-textview/ios/SNTextView/SNTextViewManager.m @@ -0,0 +1,61 @@ +// +// SNTextViewManager.m +// SNTextView +// +// Created by mo on 9/25/17. +// Copyright © 2017 standardnotes. All rights reserved. +// + +#import "SNTextViewManager.h" +#import "SNTextView.h" +#import + +@implementation SNTextViewManager + +RCT_EXPORT_MODULE() +RCT_EXPORT_VIEW_PROPERTY(text, NSString) +RCT_EXPORT_VIEW_PROPERTY(onChangeText, RCTBubblingEventBlock) +RCT_REMAP_VIEW_PROPERTY(keyboardDismissMode, keyboardDismissMode, UIScrollViewKeyboardDismissMode) +RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor) +RCT_REMAP_VIEW_PROPERTY(selectionColor, tintColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(paddingTop, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(paddingRight, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(paddingBottom, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(paddingLeft, CGFloat) + +- (UIView *)view +{ + SNTextView *textView = [[SNTextView alloc] init]; + textView.delegate = self; + return textView; +} + +RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, SNTextView) +{ + view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; +} +RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused SNTextView) +{ + view.font = [RCTFont updateFont:view.font withWeight:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused SNTextView) +{ + view.font = [RCTFont updateFont:view.font withStyle:json]; // defaults to normal +} +RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, SNTextView) +{ + view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName]; +} + +# pragma Text View Delegate + +- (void)textViewDidBeginEditing:(UITextView *)textView { + +} + +- (void)textViewDidChange:(SNTextView *)textView +{ + textView.onChangeText(@{@"text" : textView.text}); +} + +@end diff --git a/vendor/sn-textview/src/TextView.js b/vendor/sn-textview/src/TextView.js index 123d67c0..76afa9aa 100644 --- a/vendor/sn-textview/src/TextView.js +++ b/vendor/sn-textview/src/TextView.js @@ -1,14 +1,14 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import {requireNativeComponent, View, TextInput, findNodeHandle, UIManager} from 'react-native'; +import {requireNativeComponent, View, TextInput, findNodeHandle, UIManager, Platform} from 'react-native'; -export default class TextView extends Component { +export default class TextView extends TextInput { constructor(props) { super(props); } onChangeText = (event) => { - this.props.onChangeText(event.nativeEvent.message); + this.props.onChangeText(event.nativeEvent.text); } blur() { @@ -16,7 +16,26 @@ export default class TextView extends Component { } render() { - return this.ref = ref} {...this.props} onChangeTextValue={this.onChangeText} /> + if(Platform.OS == "android") { + const container = + this.ref = ref} + text={this.props.value} + onChangeText={this.onChangeText} + /> + return container; + } else { + return ( + this.ref = ref} + text={this.props.value} + onChangeText={this.onChangeText} + /> + ) + + } } } @@ -24,6 +43,11 @@ TextView.propTypes = { onChangeText: PropTypes.func, text: PropTypes.string, autoFocus: PropTypes.bool, + keyboardDismissMode: PropTypes.oneOf([ + 'none', // default + 'on-drag', // Cross-platform + 'interactive', // iOS-only + ]), ...TextInput.propTypes }