From a858144e874da3f04e3f6ac86f4b06c85172c0e5 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:03:14 -0400 Subject: [PATCH 01/50] Enabling scroll --- ios/RNTAztecView/RCTAztecViewManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index bb445c4..4500cc5 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -39,7 +39,7 @@ public class RCTAztecViewManager: RCTViewManager { defaultParagraphStyle: .default, defaultMissingImage: UIImage()) - view.isScrollEnabled = false + view.isScrollEnabled = true view.textAttachmentDelegate = attachmentDelegate if let imageProvider = imageProvider { From 7603a19c97bcbfe808850f0b8cdf72963077cf12 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:34:56 -0400 Subject: [PATCH 02/50] Commenting out missing method in Swift... --- ios/RNTAztecView/HeadingBlockFormatHandler.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/RNTAztecView/HeadingBlockFormatHandler.swift b/ios/RNTAztecView/HeadingBlockFormatHandler.swift index 214f513..b9bb9c0 100644 --- a/ios/RNTAztecView/HeadingBlockFormatHandler.swift +++ b/ios/RNTAztecView/HeadingBlockFormatHandler.swift @@ -11,15 +11,15 @@ struct HeadingBlockFormatHandler: BlockFormatHandler { } self.level = level headerFormatter = HeaderFormatter(headerLevel: level) - } + } func forceTypingFormat(on textView: RCTAztecView) { - var attributes = textView.typingAttributesSwifted - - attributes = paragraphFormatter.remove(from: attributes) - attributes = headerFormatter.apply(to: attributes, andStore: nil) - - textView.typingAttributesSwifted = attributes +// var attributes = textView.typingAttributesSwifted +// +// attributes = paragraphFormatter.remove(from: attributes) +// attributes = headerFormatter.apply(to: attributes, andStore: nil) +// +// textView.typingAttributesSwifted = attributes } private static func headerLevel(from levelString: String) -> Header.HeaderType? { From fc6ce908b0cb3049b406ce06e4d2fedafc93b616 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:35:07 -0400 Subject: [PATCH 03/50] Updating TextInputState --- src/AztecView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AztecView.js b/src/AztecView.js index f1cb465..4547b17 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import ReactNative, {requireNativeComponent, ViewPropTypes, UIManager, ColorPropType, TouchableWithoutFeedback} from 'react-native'; -import TextInputState from 'react-native/lib/TextInputState'; +import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState'; const AztecManager = UIManager.RCTAztecView; From 8742b8e63d42c30d2f8bd1d1f59b7ba90acf73a6 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:35:23 -0400 Subject: [PATCH 04/50] Not intercepting enter --- ios/RNTAztecView/RCTAztecView.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index 3eafeef..bbe3126 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -44,6 +44,8 @@ class RCTAztecView: Aztec.TextView { override init(defaultFont: UIFont, defaultParagraphStyle: ParagraphStyle, defaultMissingImage: UIImage) { super.init(defaultFont: defaultFont, defaultParagraphStyle: defaultParagraphStyle, defaultMissingImage: defaultMissingImage) commonInit() + + } required init?(coder aDecoder: NSCoder) { @@ -87,9 +89,9 @@ class RCTAztecView: Aztec.TextView { // MARK: - Edits open override func insertText(_ text: String) { - guard !interceptEnter(text) else { - return - } +// guard !interceptEnter(text) else { +// return +// } super.insertText(text) updatePlaceholderVisibility() From f3ab76344f936a0042422eb036537ecb28bd4225 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:35:47 -0400 Subject: [PATCH 05/50] Adding default MediaProvider --- ios/RNTAztecView.xcodeproj/project.pbxproj | 4 ++ ios/RNTAztecView/MediaProvider.swift | 59 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 ios/RNTAztecView/MediaProvider.swift diff --git a/ios/RNTAztecView.xcodeproj/project.pbxproj b/ios/RNTAztecView.xcodeproj/project.pbxproj index 27063dc..b6e21c3 100644 --- a/ios/RNTAztecView.xcodeproj/project.pbxproj +++ b/ios/RNTAztecView.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 397407BB263770E700DE50C1 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397407BA263770E600DE50C1 /* MediaProvider.swift */; }; 7ECFA93C21C39B5000FC131B /* HeadingBlockFormatHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECFA93B21C39B5000FC131B /* HeadingBlockFormatHandler.swift */; }; 7ECFA94021C39BA000FC131B /* BlockFormatHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECFA93F21C39BA000FC131B /* BlockFormatHandler.swift */; }; 7ECFA94221C39BBB00FC131B /* BlockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECFA94121C39BBB00FC131B /* BlockModel.swift */; }; @@ -53,6 +54,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 397407BA263770E600DE50C1 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = ""; }; 7ECFA93B21C39B5000FC131B /* HeadingBlockFormatHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadingBlockFormatHandler.swift; sourceTree = ""; }; 7ECFA93F21C39BA000FC131B /* BlockFormatHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockFormatHandler.swift; sourceTree = ""; }; 7ECFA94121C39BBB00FC131B /* BlockModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockModel.swift; sourceTree = ""; }; @@ -105,6 +107,7 @@ 7ECFA94121C39BBB00FC131B /* BlockModel.swift */, 7ECFA93B21C39B5000FC131B /* HeadingBlockFormatHandler.swift */, 7ECFA93F21C39BA000FC131B /* BlockFormatHandler.swift */, + 397407BA263770E600DE50C1 /* MediaProvider.swift */, ); path = RNTAztecView; sourceTree = ""; @@ -217,6 +220,7 @@ buildActionMask = 2147483647; files = ( 7ECFA94021C39BA000FC131B /* BlockFormatHandler.swift in Sources */, + 397407BB263770E700DE50C1 /* MediaProvider.swift in Sources */, 7ECFA93C21C39B5000FC131B /* HeadingBlockFormatHandler.swift in Sources */, F13BF4A020ECF5450047D3F9 /* RCTAztecViewManager.swift in Sources */, F1A879D020EE90C000FABD31 /* RCTAztecView.swift in Sources */, diff --git a/ios/RNTAztecView/MediaProvider.swift b/ios/RNTAztecView/MediaProvider.swift new file mode 100644 index 0000000..0cca899 --- /dev/null +++ b/ios/RNTAztecView/MediaProvider.swift @@ -0,0 +1,59 @@ +import Aztec +import Foundation + +class MediaProvider: Aztec.TextViewAttachmentDelegate { + + func textView(_ textView: TextView, attachment: NSTextAttachment, imageAt url: URL, onSuccess success: @escaping (UIImage) -> Void, onFailure failure: @escaping () -> Void) { + + DispatchQueue.main.async { + let image = UIImage(named: "BootSplashLogo")! + + success(image) + } + } + + func textView(_ textView: TextView, urlFor imageAttachment: ImageAttachment) -> URL? { + return URL(string: "www.google.com") + } + + func textView(_ textView: TextView, placeholderFor attachment: NSTextAttachment) -> UIImage { + return UIImage(named: "BootSplashLogo")! + } + + func textView(_ textView: TextView, deletedAttachment attachment: MediaAttachment) { + textView.setNeedsDisplay() + } + + func textView(_ textView: TextView, selected attachment: NSTextAttachment, atPosition position: CGPoint) { + } + + func textView(_ textView: TextView, deselected attachment: NSTextAttachment, atPosition position: CGPoint) { + } + + +} + +extension MediaProvider: Aztec.TextViewAttachmentImageProvider { + func textView(_ textView: TextView, shouldRender attachment: NSTextAttachment) -> Bool { + return true + } + + func textView(_ textView: TextView, boundsFor attachment: NSTextAttachment, with lineFragment: CGRect) -> CGRect { + return CGRect(x: 0, y: 0, width: 20, height: 20) + } + + func textView(_ textView: TextView, imageFor attachment: NSTextAttachment, with size: CGSize) -> UIImage? { + let image = UIImage(named: "BootSplashLogo")! + + return imageWithImage(image: image, scaledToSize: size) + } + + func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{ + UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0); + image.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize.width, height: newSize.height))) + let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return newImage + } + +} From 3c641c23e3b21814523ef300d8dda68ceb0ea9ac Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 26 Apr 2021 18:36:02 -0400 Subject: [PATCH 06/50] Defaulting attachmentDelegate and imageProvider... --- ios/RNTAztecView/RCTAztecViewManager.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index 4500cc5..e5c8c78 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -40,12 +40,23 @@ public class RCTAztecViewManager: RCTViewManager { defaultMissingImage: UIImage()) view.isScrollEnabled = true + + let defaultMediaProvider = MediaProvider() + + if (attachmentDelegate == nil) { + attachmentDelegate = defaultMediaProvider + } + + if (imageProvider == nil) { + imageProvider = defaultMediaProvider + } view.textAttachmentDelegate = attachmentDelegate + if let imageProvider = imageProvider { view.registerAttachmentImageProvider(imageProvider) } - + return view } From e25882cf214683d6dfb4dc7319a48f8ddb742667 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 27 Apr 2021 13:24:25 -0400 Subject: [PATCH 07/50] Removing autocorrection --- ios/RNTAztecView/RCTAztecViewManager.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index e5c8c78..284ef4c 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -41,6 +41,8 @@ public class RCTAztecViewManager: RCTViewManager { view.isScrollEnabled = true + view.autocorrectionType = .no + let defaultMediaProvider = MediaProvider() if (attachmentDelegate == nil) { From 750c5c86d7dcf5e0f8596acd60c67d8b226b2c75 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 27 Apr 2021 14:38:13 -0400 Subject: [PATCH 08/50] Implementing hiding keyboard --- ios/RNTAztecView/RCTAztecView.swift | 5 +++++ ios/RNTAztecView/RCTAztecViewManager.m | 1 + ios/RNTAztecView/RCTAztecViewManager.swift | 7 +++++++ src/AztecView.js | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index bbe3126..0a2335e 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -236,6 +236,11 @@ class RCTAztecView: Aztec.TextView { } removeLink(inRange: expandedRange) } + + @objc + func hideKeyboard() { + self.resignFirstResponder() + } func linkAttributes() -> [String: Any] { var attributes: [String: Any] = ["isActive": false] diff --git a/ios/RNTAztecView/RCTAztecViewManager.m b/ios/RNTAztecView/RCTAztecViewManager.m index 3a8858e..3cc277f 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.m +++ b/ios/RNTAztecView/RCTAztecViewManager.m @@ -21,5 +21,6 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject) RCT_EXTERN_METHOD(applyFormat:(nonnull NSNumber *)node format:(NSString *)format) RCT_EXTERN_METHOD(setLink:(nonnull NSNumber *)node url:(nonnull NSString *)url title:(nullable NSString *)title) RCT_EXTERN_METHOD(removeLink:(nonnull NSNumber *)node) +RCT_EXTERN_METHOD(hideKeyboard:(nonnull NSNumber *)node) @end diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index 284ef4c..314e0bb 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -31,6 +31,13 @@ public class RCTAztecViewManager: RCTViewManager { aztecView.setLink(with: url, and: title) }, onNode: node) } + + @objc + func hideKeyboard(_ node: NSNumber) { + executeBlock({ (aztecView) in + aztecView.hideKeyboard() + }, onNode: node) + } @objc public override func view() -> UIView { diff --git a/src/AztecView.js b/src/AztecView.js index 4547b17..563e88c 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -54,6 +54,10 @@ class AztecView extends React.Component { this.dispatch(AztecManager.Commands.setLink, [url, title]) } + hideKeyboard() { + this.dispatch(AztecManager.Commands.hideKeyboard) + } + requestHTMLWithCursor() { this.dispatch(AztecManager.Commands.returnHTMLWithCursor) } From 3d7eba72152c526a158f883729025dc6c93af33c Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 08:26:32 -0400 Subject: [PATCH 09/50] requireNativeComponent once --- src/AztecView.js | 1 + src/RCTAztecView.js | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 src/RCTAztecView.js diff --git a/src/AztecView.js b/src/AztecView.js index 563e88c..e2037a4 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import ReactNative, {requireNativeComponent, ViewPropTypes, UIManager, ColorPropType, TouchableWithoutFeedback} from 'react-native'; import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState'; +import RCTAztecView from './RCTAztecView'; const AztecManager = UIManager.RCTAztecView; diff --git a/src/RCTAztecView.js b/src/RCTAztecView.js new file mode 100644 index 0000000..28ee0c9 --- /dev/null +++ b/src/RCTAztecView.js @@ -0,0 +1,5 @@ +import {requireNativeComponent} from 'react-native'; + +const RCTAztecView = requireNativeComponent('RCTAztecView'); + +export default RCTAztecView \ No newline at end of file From 37404519f208c06abd6a7d7179f4e6db40810f4e Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 08:53:59 -0400 Subject: [PATCH 10/50] Adding support for underline --- ios/RNTAztecView/RCTAztecView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index 0a2335e..8693f21 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -37,6 +37,7 @@ class RCTAztecView: Aztec.TextView { private let formatStringMap: [FormattingIdentifier: String] = [ .bold: "bold", .italic: "italic", + .underline: "underline", .strikethrough: "strikethrough", .link: "link", ] @@ -212,6 +213,7 @@ class RCTAztecView: Aztec.TextView { switch format { case "bold": toggleBold(range: selectedRange) case "italic": toggleItalic(range: selectedRange) + case "underline": toggleUnderline(range: selectedRange) case "strikethrough": toggleStrikethrough(range: selectedRange) default: print("Format not recognized") } From 30dd116c0b49c99627b137e2829458e5b73360c1 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 14:21:06 -0400 Subject: [PATCH 11/50] Changing source for RNTAztecView.podspec --- RNTAztecView.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RNTAztecView.podspec b/RNTAztecView.podspec index 926e71e..98caad5 100644 --- a/RNTAztecView.podspec +++ b/RNTAztecView.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.license = package['license'] s.homepage = 'https://github.com/wordpress-mobile/react-native-aztec' s.authors = 'Automattic' - s.source = { :git => 'https://github.com/wordpress-mobile/react-native-aztec.git' } + s.source = { :git => 'https://github.com/ewindso/react-native-aztec' } s.source_files = 'ios/RNTAztecView/*.{h,m,swift}' s.public_header_files = 'ios/RNTAztecView/*.h' s.requires_arc = true From bee8044f9191a90c93a11fba404ce74da978a681 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 14:24:09 -0400 Subject: [PATCH 12/50] Updating package.json --- package.json | 64 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 5fe3c65..4ff60eb 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,59 @@ { - "name": "react-native-aztec", - "version": "0.1.5", - "license": "(MPL-2.0 OR GPL-2.0)", - "scripts": { - "install-aztec-ios": "cd ./ios && carthage bootstrap --platform iOS --cache-builds", - "clean": "yarn clean-watchman; yarn clean-node; yarn clean-react; yarn clean-metro; yarn clean-jest;", - "clean-jest": "rm -rf $TMPDIR/jest_*;", - "clean-metro": "rm -rf $TMPDIR/metro-cache-*; rm -rf $TMPDIR/metro-bundler-cache-*;", - "clean-node": "rm -rf node_modules/;", - "clean-react": "rm -rf $TMPDIR/react-*; rm -rf $TMPDIR/react-native-packager-cache-*;", - "clean-watchman": "command -v watchman >/dev/null 2>&1 && watchman watch-del-all;", - "clean:install": "yarn clean && yarn install" + "_from": "git+https://github.com/ewindso/react-native-aztec.git", + "_id": "react-native-aztec@0.1.5", + "_inBundle": false, + "_integrity": "", + "_location": "/react-native-aztec", + "_phantomChildren": { + "asap": "2.0.6", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.4.0", + "object-assign": "4.1.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.23" }, - "peerDependencies": { - "react": "16.6.1", - "react-native": "0.57.5" + "_requested": { + "type": "git", + "raw": "react-native-aztec@git+https://github.com/ewindso/react-native-aztec.git", + "name": "react-native-aztec", + "escapedName": "react-native-aztec", + "rawSpec": "git+https://github.com/ewindso/react-native-aztec.git", + "saveSpec": "git+https://github.com/ewindso/react-native-aztec.git", + "fetchSpec": "https://github.com/ewindso/react-native-aztec.git", + "gitCommittish": null }, + "_requiredBy": [ + "/" + ], + "_resolved": "git+https://github.com/ewindso/react-native-aztec.git#ff1999f5e6075c4361e1b1051e66a45f7007bc6e", + "_spec": "react-native-aztec@git+https://github.com/ewindso/react-native-aztec.git", + "_where": "/Users/elijahwindsor/Github/GrowthDayMobile", + "bundleDependencies": false, "dependencies": { "prop-types": "15.6.0" }, + "deprecated": false, + "description": "# react-native-aztec", "devDependencies": { "babel-cli": "^6.26.0", "babel-preset-flow": "^6.23.0", "flow-bin": "^0.69.0" - } + }, + "license": "(MPL-2.0 OR GPL-2.0)", + "name": "react-native-aztec", + "peerDependencies": { + "react": "16.13.1", + "react-native": "0.63.2" + }, + "scripts": { + "clean": "yarn clean-watchman; yarn clean-node; yarn clean-react; yarn clean-metro; yarn clean-jest;", + "clean-jest": "rm -rf $TMPDIR/jest_*;", + "clean-metro": "rm -rf $TMPDIR/metro-cache-*; rm -rf $TMPDIR/metro-bundler-cache-*;", + "clean-node": "rm -rf node_modules/;", + "clean-react": "rm -rf $TMPDIR/react-*; rm -rf $TMPDIR/react-native-packager-cache-*;", + "clean-watchman": "command -v watchman >/dev/null 2>&1 && watchman watch-del-all;", + "clean:install": "yarn clean && yarn install", + "install-aztec-ios": "cd ./ios && carthage bootstrap --platform iOS --cache-builds" + }, + "version": "0.1.5" } From a39f38191cafc5dbf8b362927762576b48797e19 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 14:40:18 -0400 Subject: [PATCH 13/50] Cleaning up --- src/AztecView.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AztecView.js b/src/AztecView.js index e2037a4..3fc692c 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -197,6 +197,4 @@ class AztecView extends React.Component { } } -const RCTAztecView = requireNativeComponent('RCTAztecView', AztecView); - export default AztecView \ No newline at end of file From 7f516a6f680d729fb7fbfceb78cdb3ed78ed2773 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 14:52:14 -0400 Subject: [PATCH 14/50] Making attachmentDelegate and imageProvider static, so can be set easier --- ios/RNTAztecView/RCTAztecViewManager.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index 314e0bb..e22afd7 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -4,9 +4,9 @@ import Foundation @objc (RCTAztecViewManager) public class RCTAztecViewManager: RCTViewManager { - public var attachmentDelegate: Aztec.TextViewAttachmentDelegate? - public var imageProvider: Aztec.TextViewAttachmentImageProvider? - + public static var attachmentDelegate: Aztec.TextViewAttachmentDelegate? + public static var imageProvider: Aztec.TextViewAttachmentImageProvider? + public override static func requiresMainQueueSetup() -> Bool { return true } @@ -52,17 +52,17 @@ public class RCTAztecViewManager: RCTViewManager { let defaultMediaProvider = MediaProvider() - if (attachmentDelegate == nil) { - attachmentDelegate = defaultMediaProvider + if (RCTAztecViewManager.attachmentDelegate == nil) { + RCTAztecViewManager.attachmentDelegate = defaultMediaProvider } - if (imageProvider == nil) { - imageProvider = defaultMediaProvider + if (RCTAztecViewManager.imageProvider == nil) { + RCTAztecViewManager.imageProvider = defaultMediaProvider } - view.textAttachmentDelegate = attachmentDelegate + view.textAttachmentDelegate = RCTAztecViewManager.attachmentDelegate - if let imageProvider = imageProvider { + if let imageProvider = RCTAztecViewManager.imageProvider { view.registerAttachmentImageProvider(imageProvider) } From fd41349492e4460570b38246f60ab587ec2fef0a Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 15:36:08 -0400 Subject: [PATCH 15/50] Defaulting to empty images... --- ios/RNTAztecView/MediaProvider.swift | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/ios/RNTAztecView/MediaProvider.swift b/ios/RNTAztecView/MediaProvider.swift index 0cca899..37875ef 100644 --- a/ios/RNTAztecView/MediaProvider.swift +++ b/ios/RNTAztecView/MediaProvider.swift @@ -6,7 +6,7 @@ class MediaProvider: Aztec.TextViewAttachmentDelegate { func textView(_ textView: TextView, attachment: NSTextAttachment, imageAt url: URL, onSuccess success: @escaping (UIImage) -> Void, onFailure failure: @escaping () -> Void) { DispatchQueue.main.async { - let image = UIImage(named: "BootSplashLogo")! + let image = UIImage() success(image) } @@ -17,7 +17,7 @@ class MediaProvider: Aztec.TextViewAttachmentDelegate { } func textView(_ textView: TextView, placeholderFor attachment: NSTextAttachment) -> UIImage { - return UIImage(named: "BootSplashLogo")! + return UIImage() } func textView(_ textView: TextView, deletedAttachment attachment: MediaAttachment) { @@ -39,21 +39,23 @@ extension MediaProvider: Aztec.TextViewAttachmentImageProvider { } func textView(_ textView: TextView, boundsFor attachment: NSTextAttachment, with lineFragment: CGRect) -> CGRect { - return CGRect(x: 0, y: 0, width: 20, height: 20) + return CGRect(x: 0, y: 0, width: 0, height: 0) } func textView(_ textView: TextView, imageFor attachment: NSTextAttachment, with size: CGSize) -> UIImage? { - let image = UIImage(named: "BootSplashLogo")! + let image = UIImage() - return imageWithImage(image: image, scaledToSize: size) + return image; + +// return imageWithImage(image: image, scaledToSize: size) } - func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{ - UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0); - image.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize.width, height: newSize.height))) - let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()! - UIGraphicsEndImageContext() - return newImage - } +// func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{ +// UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0); +// image.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize.width, height: newSize.height))) +// let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()! +// UIGraphicsEndImageContext() +// return newImage +// } } From 733f60a9671e21f8c769b6040c9ddbd16bffc133 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 28 Apr 2021 22:08:23 -0400 Subject: [PATCH 16/50] Updating fonts --- ios/RNTAztecView/RCTAztecViewManager.swift | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index e22afd7..dcaffe5 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -7,6 +7,9 @@ public class RCTAztecViewManager: RCTViewManager { public static var attachmentDelegate: Aztec.TextViewAttachmentDelegate? public static var imageProvider: Aztec.TextViewAttachmentImageProvider? + @objc + public static var defaultFont: UIFont? + public override static func requiresMainQueueSetup() -> Bool { return true } @@ -41,6 +44,9 @@ public class RCTAztecViewManager: RCTViewManager { @objc public override func view() -> UIView { + if (RCTAztecViewManager.defaultFont == nil) { + RCTAztecViewManager.defaultFont = UIFont(name: "Inter-Regular", size: 16.0); + } let view = RCTAztecView( defaultFont: defaultFont, defaultParagraphStyle: .default, @@ -79,20 +85,20 @@ public class RCTAztecViewManager: RCTViewManager { } } - private var defaultFont: UIFont { - if let font = UIFont(name: "NotoSerif", size: 16) { - return font - } + private var defaultFont: UIFont { + if let font = UIFont(name: "NotoSerif", size: 16) { + return font + } - let defaultFont = UIFont.systemFont(ofSize: 16) - guard let url = Bundle.main.url(forResource: "NotoSerif-Regular", withExtension: "ttf") else { - return defaultFont - } - CTFontManagerRegisterFontsForURL(url as CFURL, CTFontManagerScope.process, nil) - if let font = UIFont(name: "NotoSerif", size: 16) { - return font - } + let defaultFont = UIFont.systemFont(ofSize: 16) + guard let url = Bundle.main.url(forResource: "NotoSerif-Regular", withExtension: "ttf") else { + return defaultFont + } + CTFontManagerRegisterFontsForURL(url as CFURL, CTFontManagerScope.process, nil) + if let font = UIFont(name: "NotoSerif", size: 16) { + return font + } - return defaultFont - } + return defaultFont + } } From 6658ee5ee5dc98e80a8910972caceb2b8777c729 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Thu, 29 Apr 2021 09:06:06 -0400 Subject: [PATCH 17/50] Adding proper blur / focus --- ios/RNTAztecView/RCTAztecViewManager.m | 3 ++- ios/RNTAztecView/RCTAztecViewManager.swift | 25 ++++++++++++++++------ src/AztecView.js | 19 ++++++++-------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.m b/ios/RNTAztecView/RCTAztecViewManager.m index 3cc277f..e3fbda7 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.m +++ b/ios/RNTAztecView/RCTAztecViewManager.m @@ -21,6 +21,7 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject) RCT_EXTERN_METHOD(applyFormat:(nonnull NSNumber *)node format:(NSString *)format) RCT_EXTERN_METHOD(setLink:(nonnull NSNumber *)node url:(nonnull NSString *)url title:(nullable NSString *)title) RCT_EXTERN_METHOD(removeLink:(nonnull NSNumber *)node) -RCT_EXTERN_METHOD(hideKeyboard:(nonnull NSNumber *)node) +RCT_EXTERN_METHOD(focusTextInput:(nonnull NSNumber *)node) +RCT_EXTERN_METHOD(blurTextInput:(nonnull NSNumber *)node) @end diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index dcaffe5..ce7a5f3 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -20,6 +20,20 @@ public class RCTAztecViewManager: RCTViewManager { aztecView.apply(format: format) }, onNode: node) } + + @objc + func focusTextInput(_ node: NSNumber) { + executeBlock({ (aztecView) in + aztecView.becomeFirstResponder() + }, onNode: node) + } + + @objc + func blurTextInput(_ node: NSNumber) { + executeBlock({ (aztecView) in + aztecView.hideKeyboard() + }, onNode: node) + } @objc func removeLink(_ node: NSNumber) { @@ -35,13 +49,6 @@ public class RCTAztecViewManager: RCTViewManager { }, onNode: node) } - @objc - func hideKeyboard(_ node: NSNumber) { - executeBlock({ (aztecView) in - aztecView.hideKeyboard() - }, onNode: node) - } - @objc public override func view() -> UIView { if (RCTAztecViewManager.defaultFont == nil) { @@ -71,6 +78,10 @@ public class RCTAztecViewManager: RCTViewManager { if let imageProvider = RCTAztecViewManager.imageProvider { view.registerAttachmentImageProvider(imageProvider) } + + if #available(iOS 13, *) { + view.overrideUserInterfaceStyle = .light + } return view } diff --git a/src/AztecView.js b/src/AztecView.js index 3fc692c..8895e7f 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -43,6 +43,8 @@ class AztecView extends React.Component { ); } + _inputRef = null; + applyFormat(format) { this.dispatch(AztecManager.Commands.applyFormat, [format]) } @@ -55,8 +57,12 @@ class AztecView extends React.Component { this.dispatch(AztecManager.Commands.setLink, [url, title]) } - hideKeyboard() { - this.dispatch(AztecManager.Commands.hideKeyboard) + focus() { + this.dispatch(AztecManager.Commands.focusTextInput) + } + + blur() { + this.dispatch(AztecManager.Commands.blurTextInput) } requestHTMLWithCursor() { @@ -156,14 +162,6 @@ class AztecView extends React.Component { } } - blur = () => { - TextInputState.blurTextInput(ReactNative.findNodeHandle(this)); - } - - focus = () => { - TextInputState.focusTextInput(ReactNative.findNodeHandle(this)); - } - isFocused = () => { const focusedField = TextInputState.currentlyFocusedField(); return focusedField && ( focusedField === ReactNative.findNodeHandle(this) ); @@ -179,6 +177,7 @@ class AztecView extends React.Component { return ( this._inputRef = ref} onActiveFormatsChange={ this._onActiveFormatsChange } onActiveFormatAttributesChange={ this._onActiveFormatAttributesChange } onContentSizeChange = { this._onContentSizeChange } From 3558efe483011282e7d1f73fce31a6168c0117df Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Thu, 29 Apr 2021 09:10:45 -0400 Subject: [PATCH 18/50] Removing call to TextInputState --- src/AztecView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AztecView.js b/src/AztecView.js index 8895e7f..918f991 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -137,7 +137,6 @@ class AztecView extends React.Component { _onBlur = (event) => { this.selectionEndCaretY = null; - TextInputState.blurTextInput(ReactNative.findNodeHandle(this)); if (!this.props.onBlur) { return; From c441a40aedd837b8087b883088714ede7d5351a7 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 30 Apr 2021 11:46:48 -0400 Subject: [PATCH 19/50] Handling paste! --- ios/RNTAztecView/RCTAztecView.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index 8693f21..45e7bb4 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -308,9 +308,20 @@ extension RCTAztecView: UITextViewDelegate { } func textViewDidChange(_ textView: UITextView) { - forceTypingAttributesIfNeeded() - propagateFormatChanges() - propagateContentChanges() +// forceTypingAttributesIfNeeded() +// propagateFormatChanges() +// propagateContentChanges() + } + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.forceTypingAttributesIfNeeded() + self.propagateFormatChanges() + self.propagateContentChanges() + } + + return true } func textViewDidBeginEditing(_ textView: UITextView) { From 5f16c09e3b97579f72bb011d5d1d2870d299205f Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 30 Apr 2021 12:13:12 -0400 Subject: [PATCH 20/50] Focusing at end of document --- ios/RNTAztecView/RCTAztecViewManager.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index ce7a5f3..87c4aa3 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -25,6 +25,9 @@ public class RCTAztecViewManager: RCTViewManager { func focusTextInput(_ node: NSNumber) { executeBlock({ (aztecView) in aztecView.becomeFirstResponder() + + let newPosition = aztecView.endOfDocument + aztecView.selectedTextRange = aztecView.textRange(from: newPosition, to: newPosition) }, onNode: node) } @@ -34,7 +37,7 @@ public class RCTAztecViewManager: RCTViewManager { aztecView.hideKeyboard() }, onNode: node) } - + @objc func removeLink(_ node: NSNumber) { executeBlock({ (aztecView) in From 2ca7f76b0ebd096af3ec8062da876c75d8e3cdb7 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 30 Apr 2021 12:13:37 -0400 Subject: [PATCH 21/50] Propogating format changes from didChange --- ios/RNTAztecView/RCTAztecView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index 45e7bb4..c5c8453 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -308,8 +308,8 @@ extension RCTAztecView: UITextViewDelegate { } func textViewDidChange(_ textView: UITextView) { -// forceTypingAttributesIfNeeded() -// propagateFormatChanges() + forceTypingAttributesIfNeeded() + propagateFormatChanges() // propagateContentChanges() } From 4ad5c9527f8950bdd2c87b18aa4e9751dd4c4539 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 30 Apr 2021 12:35:50 -0400 Subject: [PATCH 22/50] Adding setHTML method --- ios/RNTAztecView/RCTAztecViewManager.m | 1 + ios/RNTAztecView/RCTAztecViewManager.swift | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/ios/RNTAztecView/RCTAztecViewManager.m b/ios/RNTAztecView/RCTAztecViewManager.m index e3fbda7..f7c733d 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.m +++ b/ios/RNTAztecView/RCTAztecViewManager.m @@ -23,5 +23,6 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject) RCT_EXTERN_METHOD(removeLink:(nonnull NSNumber *)node) RCT_EXTERN_METHOD(focusTextInput:(nonnull NSNumber *)node) RCT_EXTERN_METHOD(blurTextInput:(nonnull NSNumber *)node) +RCT_EXTERN_METHOD(setHTML:(nonnull NSNumber *)node html:(NSString *)html) @end diff --git a/ios/RNTAztecView/RCTAztecViewManager.swift b/ios/RNTAztecView/RCTAztecViewManager.swift index 87c4aa3..ea937f9 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.swift +++ b/ios/RNTAztecView/RCTAztecViewManager.swift @@ -30,6 +30,14 @@ public class RCTAztecViewManager: RCTViewManager { aztecView.selectedTextRange = aztecView.textRange(from: newPosition, to: newPosition) }, onNode: node) } + + @objc + func setHTML(_ node: NSNumber, html: String) { + executeBlock({ (aztecView) in + aztecView.setHTML(html) + aztecView.updatePlaceholderVisibility() + }, onNode: node) + } @objc func blurTextInput(_ node: NSNumber) { @@ -52,6 +60,8 @@ public class RCTAztecViewManager: RCTViewManager { }, onNode: node) } + + @objc public override func view() -> UIView { if (RCTAztecViewManager.defaultFont == nil) { From 8c977860b8b224bc437ae980510f21fe2fb2bfd7 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 30 Apr 2021 16:42:12 -0400 Subject: [PATCH 23/50] Adding setHTML, focusEndOfDocument --- src/AztecView.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/AztecView.js b/src/AztecView.js index 918f991..7d038cc 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -58,6 +58,9 @@ class AztecView extends React.Component { } focus() { + } + + focusEndOfDocument() { this.dispatch(AztecManager.Commands.focusTextInput) } @@ -65,6 +68,10 @@ class AztecView extends React.Component { this.dispatch(AztecManager.Commands.blurTextInput) } + setHTML(html) { + this.dispatch(AztecManager.Commands.setHTML, [html]) + } + requestHTMLWithCursor() { this.dispatch(AztecManager.Commands.returnHTMLWithCursor) } From 926144e575750bde5d68352d8d9b29f260da7cb3 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Sun, 2 May 2021 18:57:23 -0400 Subject: [PATCH 24/50] Changing deployment target to 11.0 --- ios/RNTAztecView.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/RNTAztecView.xcodeproj/project.pbxproj b/ios/RNTAztecView.xcodeproj/project.pbxproj index b6e21c3..3bf45e8 100644 --- a/ios/RNTAztecView.xcodeproj/project.pbxproj +++ b/ios/RNTAztecView.xcodeproj/project.pbxproj @@ -291,7 +291,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -345,7 +345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; From bc4b3d74cd95f76cd724f17099b59015179a4194 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 3 May 2021 13:16:38 -0400 Subject: [PATCH 25/50] Updating to 11.0 --- RNTAztecView.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RNTAztecView.podspec b/RNTAztecView.podspec index 98caad5..7d079bf 100644 --- a/RNTAztecView.podspec +++ b/RNTAztecView.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'ios/RNTAztecView/*.{h,m,swift}' s.public_header_files = 'ios/RNTAztecView/*.h' s.requires_arc = true - s.platforms = { :ios => "10.0" } + s.platforms = { :ios => "11.0" } s.xcconfig = {'OTHER_LDFLAGS' => '-lxml2', 'HEADER_SEARCH_PATHS' => '/usr/include/libxml2'} s.dependency 'React' From 378b826650175c59e12febd2d47eecf430aaa92b Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 7 May 2021 15:04:50 -0400 Subject: [PATCH 26/50] Catching issue where dom node cannot be found --- src/AztecView.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/AztecView.js b/src/AztecView.js index 7d038cc..cac5782 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -34,13 +34,18 @@ class AztecView extends React.Component { ...ViewPropTypes, // include the default view properties } - dispatch(command, params) { + dispatch = (command, params) => { params = params || []; - UIManager.dispatchViewManagerCommand( - ReactNative.findNodeHandle(this), - command, - params, - ); + + try { + UIManager.dispatchViewManagerCommand( + ReactNative.findNodeHandle(this), + command, + params, + ); + } catch(error) { + console.log({error}) + } } _inputRef = null; From d3a3bd4684ae64eb14cb4f6dca985e00cb6ea009 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 10 May 2021 18:00:05 -0400 Subject: [PATCH 27/50] Updating android aztec version --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index aae3b77..2cc6d52 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -12,7 +12,7 @@ buildscript { wordpressUtilsVersion = '1.22' espressoVersion = '3.0.1' - aztecVersion = 'v1.3.14' + aztecVersion = 'v1.3.45' } repositories { From fe13d1c2073ebccddeb9074dd4e39a5ab064e18d Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 11 May 2021 09:16:03 -0400 Subject: [PATCH 28/50] Updating ReactAztecText onEnterKey --- .../wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 7a58d09..db2e34f 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -2,11 +2,12 @@ import android.content.Context; import android.graphics.Rect; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; import android.view.inputmethod.InputMethodManager; +import android.text.Spannable; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; @@ -56,7 +57,7 @@ public ReactAztecText(ThemedReactContext reactContext) { super(reactContext); this.setAztecKeyListener(new ReactAztecText.OnAztecKeyListener() { @Override - public boolean onEnterKey() { + public boolean onEnterKey(Spannable text, boolean firedAfterTextChanged, int selStart, int selEnd) { if (shouldHandleOnEnter) { return onEnter(); } From d32b35a2959247d0040e5ef560575544f0ef95a5 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 11 May 2021 14:04:57 -0400 Subject: [PATCH 29/50] Updating Gravity on ReactAztecText to be at the top / start --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index db2e34f..7b69417 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -8,6 +8,7 @@ import android.text.TextWatcher; import android.view.inputmethod.InputMethodManager; import android.text.Spannable; +import android.view.Gravity; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; @@ -20,6 +21,7 @@ import com.facebook.react.views.textinput.ScrollWatcher; import org.wordpress.aztec.AztecText; +import org.wordpress.aztec.AlignmentRendering; import org.wordpress.aztec.AztecTextFormat; import org.wordpress.aztec.ITextFormat; import org.wordpress.aztec.plugins.IAztecPlugin; @@ -81,6 +83,7 @@ public void onSelectionChanged(int selStart, int selEnd) { } }); this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + this.setGravity(Gravity.TOP | Gravity.START); } @Override From e84da8059071bfe1c270b9c80c3f9591c2f77716 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 11 May 2021 14:29:28 -0400 Subject: [PATCH 30/50] Moving cursor to end of the TextEdit on JS focus --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 7b69417..382d90f 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -131,6 +131,7 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { }*/ setFocusableInTouchMode(true); boolean focused = super.requestFocus(direction, previouslyFocusedRect); + super.setSelection(this.length()); showSoftKeyboard(); return focused; } From 265852e54f17d94f9132c0be1c20cbf08c2f5ebb Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 11 May 2021 15:40:19 -0400 Subject: [PATCH 31/50] Scrolling to bottom when JS focuses on Aztec --- .../wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 382d90f..17eb0ee 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -131,7 +131,14 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { }*/ setFocusableInTouchMode(true); boolean focused = super.requestFocus(direction, previouslyFocusedRect); + + final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight(); + if (scrollAmount > 0) { + this.scrollTo(0, scrollAmount); + } + super.setSelection(this.length()); + showSoftKeyboard(); return focused; } From 6eb4eacf6662c1276dd8572848d014ad2623bfb5 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 12 May 2021 14:41:23 -0400 Subject: [PATCH 32/50] Adding support for underline --- .../wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 17eb0ee..f740894 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -206,6 +206,10 @@ private void updateToolbarButtons(ArrayList appliedStyles) { if (currentStyle == AztecTextFormat.FORMAT_STRIKETHROUGH) { formattingOptions.add("strikethrough"); } + + if (currentStyle == AztecTextFormat.FORMAT_UNDERLINE) { + formattingOptions.add("underline"); + } } // Check if the same formatting event was already sent @@ -343,6 +347,9 @@ public void applyFormat(String format) { case ("strikethrough"): newFormats.add(AztecTextFormat.FORMAT_STRIKETHROUGH); break; + case ("underline"): + newFormats.add(AztecTextFormat.FORMAT_UNDERLINE); + break; } if (newFormats.size() == 0) { From 01ee26a4c671f58ce001157b0c1037ddfcf7cdcb Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 18 May 2021 16:34:41 -0400 Subject: [PATCH 33/50] Adding setHTML for Android --- .../mobile/ReactNativeAztec/ReactAztecManager.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index dbee8a6..438d321 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -3,7 +3,7 @@ import android.graphics.Color; import android.graphics.Typeface; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -51,6 +51,7 @@ public class ReactAztecManager extends SimpleViewManager { private static final int FOCUS_TEXT_INPUT = 1; private static final int BLUR_TEXT_INPUT = 2; + private static final int SET_HTML = 3; private static final int COMMAND_NOTIFY_APPLY_FORMAT = 100; private static final int UNSET = -1; @@ -59,6 +60,7 @@ public class ReactAztecManager extends SimpleViewManager { // see https://github.com/wordpress-mobile/react-native-aztec/pull/79 private int mFocusTextInputCommandCode = FOCUS_TEXT_INPUT; // pre-init private int mBlurTextInputCommandCode = BLUR_TEXT_INPUT; // pre-init + private int mSetHTMLCommandCode = SET_HTML; private static final String TAG = "ReactAztecText"; @@ -381,6 +383,7 @@ public Map getCommandsMap() { .put("applyFormat", COMMAND_NOTIFY_APPLY_FORMAT) .put("focusTextInput", mFocusTextInputCommandCode) .put("blurTextInput", mBlurTextInputCommandCode) + .put("setHTML", mSetHTMLCommandCode) .build(); } @@ -398,6 +401,10 @@ public void receiveCommand(final ReactAztecText parent, int commandType, @Nullab } else if (commandType == mBlurTextInputCommandCode) { parent.clearFocusFromJS(); return; + } else if (commandType == mSetHTMLCommandCode) { + final String html = args.getString(0); + setTextfromJS(parent, html); + return; } super.receiveCommand(parent, commandType, args); } From 5828d558f55cfdc3771553378d8c8943eb51304f Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Mon, 24 May 2021 18:50:26 -0400 Subject: [PATCH 34/50] Putting in conditional call to propogateContentChanges() (for formatting) --- ios/RNTAztecView/RCTAztecView.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index c5c8453..1088b14 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -2,6 +2,8 @@ import Aztec import Foundation import UIKit +var lastTimeShouldChangeCalled = 0.0; + class RCTAztecView: Aztec.TextView { @objc var onBackspace: RCTBubblingEventBlock? = nil @objc var onChange: RCTBubblingEventBlock? = nil @@ -310,10 +312,16 @@ extension RCTAztecView: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { forceTypingAttributesIfNeeded() propagateFormatChanges() -// propagateContentChanges() + + let nowTime = NSDate().timeIntervalSince1970 + + if (nowTime - Double(lastTimeShouldChangeCalled) > 0.1) { + propagateContentChanges() + } } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + lastTimeShouldChangeCalled = NSDate().timeIntervalSince1970 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.forceTypingAttributesIfNeeded() From 559d776268efd480ffeacf3dcaab66b029b68dfe Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 2 Jun 2021 17:40:35 -0400 Subject: [PATCH 35/50] Removing call to onEnter, was causing issues with Huawei --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index f740894..bd5c984 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -60,9 +60,6 @@ public ReactAztecText(ThemedReactContext reactContext) { this.setAztecKeyListener(new ReactAztecText.OnAztecKeyListener() { @Override public boolean onEnterKey(Spannable text, boolean firedAfterTextChanged, int selStart, int selEnd) { - if (shouldHandleOnEnter) { - return onEnter(); - } return false; } @Override From ee6342b6117bb9b246986d2914f5f4eaa02720b1 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 29 Jun 2021 12:55:39 -0400 Subject: [PATCH 36/50] Adding scrollToBottom command for Android only --- .../mobile/ReactNativeAztec/ReactAztecManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index 438d321..bfc456c 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -52,6 +52,7 @@ public class ReactAztecManager extends SimpleViewManager { private static final int FOCUS_TEXT_INPUT = 1; private static final int BLUR_TEXT_INPUT = 2; private static final int SET_HTML = 3; + private static final int SCROLL_TO_BOTTOM = 4; private static final int COMMAND_NOTIFY_APPLY_FORMAT = 100; private static final int UNSET = -1; @@ -61,6 +62,7 @@ public class ReactAztecManager extends SimpleViewManager { private int mFocusTextInputCommandCode = FOCUS_TEXT_INPUT; // pre-init private int mBlurTextInputCommandCode = BLUR_TEXT_INPUT; // pre-init private int mSetHTMLCommandCode = SET_HTML; + private int mScrollToBottomCode = SCROLL_TO_BOTTOM; private static final String TAG = "ReactAztecText"; @@ -384,6 +386,7 @@ public Map getCommandsMap() { .put("focusTextInput", mFocusTextInputCommandCode) .put("blurTextInput", mBlurTextInputCommandCode) .put("setHTML", mSetHTMLCommandCode) + .put("scrollToBottom", mScrollToBottomCode) .build(); } @@ -405,6 +408,9 @@ public void receiveCommand(final ReactAztecText parent, int commandType, @Nullab final String html = args.getString(0); setTextfromJS(parent, html); return; + } else if (commandType == mScrollToBottomCode) { + Log.d("SCROLLING", "1"); + parent.scrollToBottom(); } super.receiveCommand(parent, commandType, args); } From 197d83dbc54cf6da80ea9d69af7fc4ea8ea5ef06 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 29 Jun 2021 12:56:06 -0400 Subject: [PATCH 37/50] Fixing issue where it doesn't scroll down all the way (adding 50 seems to work on the Y position...) --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index bd5c984..addda29 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -131,7 +131,7 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight(); if (scrollAmount > 0) { - this.scrollTo(0, scrollAmount); + this.scrollTo(0, scrollAmount + 50); } super.setSelection(this.length()); From 4aa0b30e8de2f241563bbb6109253c2a4a8b6e92 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 29 Jun 2021 12:56:34 -0400 Subject: [PATCH 38/50] Adding scrollToBottom for Android --- .../mobile/ReactNativeAztec/ReactAztecText.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index addda29..aa2d02e 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -140,6 +140,17 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { return focused; } + public void scrollToBottom() { + final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight(); + + // only scroll if the user is already at the bottom. ignore otherwise + if (scrollAmount > 0 && this.getSelectionStart() >= this.getText().toString().length() - 1) { + this.scrollTo(0, scrollAmount + 50); + + super.setSelection(this.length()); + } + } + private boolean showSoftKeyboard() { return mInputMethodManager.showSoftInput(this, 0); } From b0b1b7f85d332ec895946348ebf18dd796ae1038 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Tue, 29 Jun 2021 12:56:53 -0400 Subject: [PATCH 39/50] Call scrollToBottom from AztecView.js in _onContentSizeChange, only for Android --- src/AztecView.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/AztecView.js b/src/AztecView.js index cac5782..f5d1995 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import ReactNative, {requireNativeComponent, ViewPropTypes, UIManager, ColorPropType, TouchableWithoutFeedback} from 'react-native'; +import ReactNative, {Platform, ViewPropTypes, UIManager, ColorPropType, TouchableWithoutFeedback} from 'react-native'; import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState'; import RCTAztecView from './RCTAztecView'; @@ -77,6 +77,12 @@ class AztecView extends React.Component { this.dispatch(AztecManager.Commands.setHTML, [html]) } + scrollToBottom() { + if (Platform.OS === 'android') { + this.dispatch(AztecManager.Commands.scrollToBottom); + } + } + requestHTMLWithCursor() { this.dispatch(AztecManager.Commands.returnHTMLWithCursor) } @@ -106,6 +112,8 @@ class AztecView extends React.Component { const size = event.nativeEvent.contentSize; const { onContentSizeChange } = this.props; onContentSizeChange(size); + + this.scrollToBottom(); } _onEnter = (event) => { From 8f7d91d1cf21a0ba79234506137fd37e92fe2f54 Mon Sep 17 00:00:00 2001 From: Mathieu Clerici Date: Mon, 26 Jul 2021 11:16:48 -0500 Subject: [PATCH 40/50] [FIX] - Fixed an android bug where html format was not applied on the last character in the onchange event. (#1) --- .../mobile/ReactNativeAztec/ReactAztecManager.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index bfc456c..b0d80d2 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -478,14 +478,6 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { return; } - // The event that contains the event counter and updates it must be sent first. - // TODO: t7936714 merge these events - mEventDispatcher.dispatchEvent( - new ReactTextChangedEvent( - mEditText.getId(), - mEditText.toHtml(false), - mEditText.incrementAndGetEventCounter())); - mEventDispatcher.dispatchEvent( new ReactTextInputEvent( mEditText.getId(), @@ -497,6 +489,11 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { @Override public void afterTextChanged(Editable s) { + mEventDispatcher.dispatchEvent( + new ReactTextChangedEvent( + mEditText.getId(), + mEditText.toHtml(false), + mEditText.incrementAndGetEventCounter())); } } From 71287f809a17b0bc30fa5e803e5b78d0552cba16 Mon Sep 17 00:00:00 2001 From: Mathieu Clerici Date: Wed, 28 Jul 2021 23:24:18 -0500 Subject: [PATCH 41/50] Fixed a bug where scrolling was not working properly. (#2) --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index aa2d02e..9caaf82 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -141,11 +141,11 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { } public void scrollToBottom() { - final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight(); + final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight() + 50; // only scroll if the user is already at the bottom. ignore otherwise if (scrollAmount > 0 && this.getSelectionStart() >= this.getText().toString().length() - 1) { - this.scrollTo(0, scrollAmount + 50); + this.scrollTo(0, scrollAmount); super.setSelection(this.length()); } From f31acaa55731ef0d4704239c3a46bb84d52e9807 Mon Sep 17 00:00:00 2001 From: Mathieu Clerici Date: Mon, 27 Sep 2021 15:01:35 -0500 Subject: [PATCH 42/50] Added support for link click while editing. (#3) --- .../BetterLinkMovementMethod.java | 455 ++++++++++++++++++ .../ReactNativeAztec/ReactAztecManager.java | 3 + .../ReactNativeAztec/ReactAztecText.java | 12 + .../res/values/betterlinkmovementmethod.xml | 4 + 4 files changed, 474 insertions(+) create mode 100644 android/src/main/java/org/wordpress/mobile/ReactNativeAztec/BetterLinkMovementMethod.java create mode 100644 android/src/main/res/values/betterlinkmovementmethod.xml diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/BetterLinkMovementMethod.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/BetterLinkMovementMethod.java new file mode 100644 index 0000000..8c7b3ef --- /dev/null +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/BetterLinkMovementMethod.java @@ -0,0 +1,455 @@ +package org.wordpress.mobile.ReactNativeAztec; + +import android.app.Activity; +import android.graphics.RectF; +import android.text.Layout; +import android.text.Selection; +import android.text.Spannable; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.BackgroundColorSpan; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +/** + * Handles URL clicks on TextViews. Unlike the default implementation, this: + *

+ *

    + *
  • Reliably applies a highlight color on links when they're touched.
  • + *
  • Let's you handle single and long clicks on URLs
  • + *
  • Correctly identifies focused URLs (Unlike the default implementation where a click is registered even if it's + * made outside of the URL's bounds if there is no more text in that direction.)
  • + *
+ */ +public class BetterLinkMovementMethod extends LinkMovementMethod { + + private static BetterLinkMovementMethod singleInstance; + private static final int LINKIFY_NONE = -2; + + private OnLinkClickListener onLinkClickListener; + private OnLinkLongClickListener onLinkLongClickListener; + private final RectF touchedLineBounds = new RectF(); + private boolean isUrlHighlighted; + private ClickableSpan clickableSpanUnderTouchOnActionDown; + private int activeTextViewHashcode; + private LongPressTimer ongoingLongPressTimer; + private boolean wasLongPressRegistered; + + public interface OnLinkClickListener { + /** + * @param textView The TextView on which a click was registered. + * @param url The clicked URL. + * @return True if this click was handled. False to let Android handle the URL. + */ + boolean onClick(TextView textView, String url); + } + + public interface OnLinkLongClickListener { + /** + * @param textView The TextView on which a long-click was registered. + * @param url The long-clicked URL. + * @return True if this long-click was handled. False to let Android handle the URL (as a short-click). + */ + boolean onLongClick(TextView textView, String url); + } + + /** + * Return a new instance of BetterLinkMovementMethod. + */ + public static BetterLinkMovementMethod newInstance() { + return new BetterLinkMovementMethod(); + } + + /** + * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, + * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. + * @param textViews The TextViews on which a {@link BetterLinkMovementMethod} should be registered. + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + public static BetterLinkMovementMethod linkify(int linkifyMask, TextView... textViews) { + BetterLinkMovementMethod movementMethod = newInstance(); + for (TextView textView : textViews) { + addLinks(linkifyMask, movementMethod, textView); + } + return movementMethod; + } + + /** + * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. + * + * @param textViews The TextViews on which a {@link BetterLinkMovementMethod} should be registered. + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + public static BetterLinkMovementMethod linkifyHtml(TextView... textViews) { + return linkify(LINKIFY_NONE, textViews); + } + + /** + * Recursively register a {@link BetterLinkMovementMethod} on every TextView inside a layout. + * + * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, + * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + public static BetterLinkMovementMethod linkify(int linkifyMask, ViewGroup viewGroup) { + BetterLinkMovementMethod movementMethod = newInstance(); + rAddLinks(linkifyMask, viewGroup, movementMethod); + return movementMethod; + } + + /** + * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. + * + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod linkifyHtml(ViewGroup viewGroup) { + return linkify(LINKIFY_NONE, viewGroup); + } + + /** + * Recursively register a {@link BetterLinkMovementMethod} on every TextView inside a layout. + * + * @param linkifyMask One of {@link Linkify#ALL}, {@link Linkify#PHONE_NUMBERS}, {@link Linkify#MAP_ADDRESSES}, + * {@link Linkify#WEB_URLS} and {@link Linkify#EMAIL_ADDRESSES}. + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + public static BetterLinkMovementMethod linkify(int linkifyMask, Activity activity) { + // Find the layout passed to setContentView(). + ViewGroup activityLayout = ((ViewGroup) ((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).getChildAt(0)); + + BetterLinkMovementMethod movementMethod = newInstance(); + rAddLinks(linkifyMask, activityLayout, movementMethod); + return movementMethod; + } + + /** + * Like {@link #linkify(int, TextView...)}, but can be used for TextViews with HTML links. + * + * @return The registered {@link BetterLinkMovementMethod} on the TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod linkifyHtml(Activity activity) { + return linkify(LINKIFY_NONE, activity); + } + + /** + * Get a static instance of BetterLinkMovementMethod. Do note that registering a click listener on the returned + * instance is not supported because it will potentially be shared on multiple TextViews. + */ + @SuppressWarnings("unused") + public static BetterLinkMovementMethod getInstance() { + if (singleInstance == null) { + singleInstance = new BetterLinkMovementMethod(); + } + return singleInstance; + } + + protected BetterLinkMovementMethod() { + } + + /** + * Set a listener that will get called whenever any link is clicked on the TextView. + */ + public BetterLinkMovementMethod setOnLinkClickListener(OnLinkClickListener clickListener) { + if (this == singleInstance) { + throw new UnsupportedOperationException("Setting a click listener on the instance returned by getInstance() is not supported to avoid memory " + + "leaks. Please use newInstance() or any of the linkify() methods instead."); + } + + this.onLinkClickListener = clickListener; + return this; + } + + /** + * Set a listener that will get called whenever any link is clicked on the TextView. + */ + public BetterLinkMovementMethod setOnLinkLongClickListener(OnLinkLongClickListener longClickListener) { + if (this == singleInstance) { + throw new UnsupportedOperationException("Setting a long-click listener on the instance returned by getInstance() is not supported to avoid " + + "memory leaks. Please use newInstance() or any of the linkify() methods instead."); + } + + this.onLinkLongClickListener = longClickListener; + return this; + } + +// ======== PUBLIC APIs END ======== // + + private static void rAddLinks(int linkifyMask, ViewGroup viewGroup, BetterLinkMovementMethod movementMethod) { + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + + if (child instanceof ViewGroup) { + // Recursively find child TextViews. + rAddLinks(linkifyMask, ((ViewGroup) child), movementMethod); + } else if (child instanceof TextView) { + TextView textView = (TextView) child; + addLinks(linkifyMask, movementMethod, textView); + } + } + } + + private static void addLinks(int linkifyMask, BetterLinkMovementMethod movementMethod, TextView textView) { + textView.setMovementMethod(movementMethod); + if (linkifyMask != LINKIFY_NONE) { + Linkify.addLinks(textView, linkifyMask); + } + } + + @Override + public boolean onTouchEvent(final TextView textView, Spannable text, MotionEvent event) { + if (activeTextViewHashcode != textView.hashCode()) { + // Bug workaround: TextView stops calling onTouchEvent() once any URL is highlighted. + // A hacky solution is to reset any "autoLink" property set in XML. But we also want + // to do this once per TextView. + activeTextViewHashcode = textView.hashCode(); + textView.setAutoLinkMask(0); + } + + final ClickableSpan clickableSpanUnderTouch = findClickableSpanUnderTouch(textView, text, event); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + clickableSpanUnderTouchOnActionDown = clickableSpanUnderTouch; + } + final boolean touchStartedOverAClickableSpan = clickableSpanUnderTouchOnActionDown != null; + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (clickableSpanUnderTouch != null) { + highlightUrl(textView, clickableSpanUnderTouch, text); + } + + if (touchStartedOverAClickableSpan && onLinkLongClickListener != null) { + LongPressTimer.OnTimerReachedListener longClickListener = new LongPressTimer.OnTimerReachedListener() { + @Override + public void onTimerReached() { + wasLongPressRegistered = true; + textView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + removeUrlHighlightColor(textView); + dispatchUrlLongClick(textView, clickableSpanUnderTouch); + } + }; + startTimerForRegisteringLongClick(textView, longClickListener); + } + return touchStartedOverAClickableSpan; + + case MotionEvent.ACTION_UP: + // Register a click only if the touch started and ended on the same URL. + if (!wasLongPressRegistered && touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) { + dispatchUrlClick(textView, clickableSpanUnderTouch); + } + cleanupOnTouchUp(textView); + + // Consume this event even if we could not find any spans to avoid letting Android handle this event. + // Android's TextView implementation has a bug where links get clicked even when there is no more text + // next to the link and the touch lies outside its bounds in the same direction. + return touchStartedOverAClickableSpan; + + case MotionEvent.ACTION_CANCEL: + cleanupOnTouchUp(textView); + return false; + + case MotionEvent.ACTION_MOVE: + // Stop listening for a long-press as soon as the user wanders off to unknown lands. + if (clickableSpanUnderTouch != clickableSpanUnderTouchOnActionDown) { + removeLongPressCallback(textView); + } + + if (!wasLongPressRegistered) { + // Toggle highlight. + if (clickableSpanUnderTouch != null) { + highlightUrl(textView, clickableSpanUnderTouch, text); + } else { + removeUrlHighlightColor(textView); + } + } + + return touchStartedOverAClickableSpan; + + default: + return false; + } + } + + private void cleanupOnTouchUp(TextView textView) { + wasLongPressRegistered = false; + clickableSpanUnderTouchOnActionDown = null; + removeUrlHighlightColor(textView); + removeLongPressCallback(textView); + } + + /** + * Determines the touched location inside the TextView's text and returns the ClickableSpan found under it (if any). + * + * @return The touched ClickableSpan or null. + */ + protected ClickableSpan findClickableSpanUnderTouch(TextView textView, Spannable text, MotionEvent event) { + // So we need to find the location in text where touch was made, regardless of whether the TextView + // has scrollable text. That is, not the entire text is currently visible. + int touchX = (int) event.getX(); + int touchY = (int) event.getY(); + + // Ignore padding. + touchX -= textView.getTotalPaddingLeft(); + touchY -= textView.getTotalPaddingTop(); + + // Account for scrollable text. + touchX += textView.getScrollX(); + touchY += textView.getScrollY(); + + final Layout layout = textView.getLayout(); + final int touchedLine = layout.getLineForVertical(touchY); + final int touchOffset = layout.getOffsetForHorizontal(touchedLine, touchX); + + touchedLineBounds.left = layout.getLineLeft(touchedLine); + touchedLineBounds.top = layout.getLineTop(touchedLine); + touchedLineBounds.right = layout.getLineWidth(touchedLine) + touchedLineBounds.left; + touchedLineBounds.bottom = layout.getLineBottom(touchedLine); + + if (touchedLineBounds.contains(touchX, touchY)) { + // Find a ClickableSpan that lies under the touched area. + final Object[] spans = text.getSpans(touchOffset, touchOffset, ClickableSpan.class); + for (final Object span : spans) { + if (span instanceof ClickableSpan) { + return (ClickableSpan) span; + } + } + // No ClickableSpan found under the touched location. + return null; + + } else { + // Touch lies outside the line's horizontal bounds where no spans should exist. + return null; + } + } + + /** + * Adds a background color span at clickableSpan's location. + */ + protected void highlightUrl(TextView textView, ClickableSpan clickableSpan, Spannable text) { + if (isUrlHighlighted) { + return; + } + isUrlHighlighted = true; + + int spanStart = text.getSpanStart(clickableSpan); + int spanEnd = text.getSpanEnd(clickableSpan); + BackgroundColorSpan highlightSpan = new BackgroundColorSpan(textView.getHighlightColor()); + text.setSpan(highlightSpan, spanStart, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE); + + textView.setTag(R.id.bettermovementmethod_highlight_background_span, highlightSpan); + + Selection.setSelection(text, spanStart, spanEnd); + } + + /** + * Removes the highlight color under the Url. + */ + protected void removeUrlHighlightColor(TextView textView) { + if (!isUrlHighlighted) { + return; + } + isUrlHighlighted = false; + + Spannable text = (Spannable) textView.getText(); + BackgroundColorSpan highlightSpan = (BackgroundColorSpan) textView.getTag(R.id.bettermovementmethod_highlight_background_span); + text.removeSpan(highlightSpan); + + Selection.removeSelection(text); + } + + protected void startTimerForRegisteringLongClick(TextView textView, LongPressTimer.OnTimerReachedListener longClickListener) { + ongoingLongPressTimer = new LongPressTimer(); + ongoingLongPressTimer.setOnTimerReachedListener(longClickListener); + textView.postDelayed(ongoingLongPressTimer, ViewConfiguration.getLongPressTimeout()); + } + + /** + * Remove the long-press detection timer. + */ + protected void removeLongPressCallback(TextView textView) { + if (ongoingLongPressTimer != null) { + textView.removeCallbacks(ongoingLongPressTimer); + ongoingLongPressTimer = null; + } + } + + protected void dispatchUrlClick(TextView textView, ClickableSpan clickableSpan) { + ClickableSpanWithText clickableSpanWithText = ClickableSpanWithText.ofSpan(textView, clickableSpan); + boolean handled = onLinkClickListener != null && onLinkClickListener.onClick(textView, clickableSpanWithText.text()); + + if (!handled) { + // Let Android handle this click. + clickableSpanWithText.span().onClick(textView); + } + } + + protected void dispatchUrlLongClick(TextView textView, ClickableSpan clickableSpan) { + ClickableSpanWithText clickableSpanWithText = ClickableSpanWithText.ofSpan(textView, clickableSpan); + boolean handled = onLinkLongClickListener != null && onLinkLongClickListener.onLongClick(textView, clickableSpanWithText.text()); + + if (!handled) { + // Let Android handle this long click as a short-click. + clickableSpanWithText.span().onClick(textView); + } + } + + protected static final class LongPressTimer implements Runnable { + private OnTimerReachedListener onTimerReachedListener; + + protected interface OnTimerReachedListener { + void onTimerReached(); + } + + @Override + public void run() { + onTimerReachedListener.onTimerReached(); + } + + public void setOnTimerReachedListener(OnTimerReachedListener listener) { + onTimerReachedListener = listener; + } + } + + /** + * A wrapper to support all {@link ClickableSpan}s that may or may not provide URLs. + */ + protected static class ClickableSpanWithText { + private ClickableSpan span; + private String text; + + protected static ClickableSpanWithText ofSpan(TextView textView, ClickableSpan span) { + Spanned s = (Spanned) textView.getText(); + String text; + if (span instanceof URLSpan) { + text = ((URLSpan) span).getURL(); + } else { + int start = s.getSpanStart(span); + int end = s.getSpanEnd(span); + text = s.subSequence(start, end).toString(); + } + return new ClickableSpanWithText(span, text); + } + + protected ClickableSpanWithText(ClickableSpan span, String text) { + this.span = span; + this.text = text; + } + + protected ClickableSpan span() { + return span; + } + + protected String text() { + return text; + } + } +} \ No newline at end of file diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index b0d80d2..dce75ef 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -6,6 +6,7 @@ import androidx.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; +import android.text.util.Linkify; import android.util.Log; import android.util.TypedValue; import android.view.View; @@ -91,6 +92,8 @@ protected ReactAztecText createViewInstance(ThemedReactContext reactContext) { aztecText.setFocusableInTouchMode(true); aztecText.setFocusable(true); aztecText.setCalypsoMode(false); + aztecText.setLinksClickable(true); + aztecText.setAutoLinkMask(Linkify.WEB_URLS); return aztecText; } diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 9caaf82..02ddf8e 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -81,6 +81,18 @@ public void onSelectionChanged(int selStart, int selEnd) { }); this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE); this.setGravity(Gravity.TOP | Gravity.START); + + BetterLinkMovementMethod linkClick = BetterLinkMovementMethod.newInstance(); + + linkClick.setOnLinkClickListener(new BetterLinkMovementMethod.OnLinkClickListener() { + @Override + public boolean onClick(TextView textView, String url) { + hideSoftKeyboard(); + return false; + } + }); + + this.setMovementMethod(linkClick); } @Override diff --git a/android/src/main/res/values/betterlinkmovementmethod.xml b/android/src/main/res/values/betterlinkmovementmethod.xml new file mode 100644 index 0000000..eac24ae --- /dev/null +++ b/android/src/main/res/values/betterlinkmovementmethod.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 2e9d057be5b19b2f5fbd7a2eb7e5198b7c38e7cb Mon Sep 17 00:00:00 2001 From: Mathieu Clerici Date: Wed, 29 Sep 2021 11:01:41 -0500 Subject: [PATCH 43/50] Fixed build error, missing import (#4) --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 02ddf8e..f0e7928 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -9,6 +9,7 @@ import android.view.inputmethod.InputMethodManager; import android.text.Spannable; import android.view.Gravity; +import android.widget.TextView; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; From c27cbbe9dcff6cbb947fd43a76824920ad04127d Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Wed, 3 Nov 2021 19:12:20 -0400 Subject: [PATCH 44/50] feat: autoCorrect (iOS works, not Android yet...) (#5) * chore: adding autoCorrect propType * chore: adding autoCorrect to RCTAztecView.swift * feat: applying autocorrectionType... * feat: updating autocorrectionType through didSetProps * feat: adding setAutoCorrect for android * chore: importing InputType * [FIX] - Fixed behavior for android autoCorrect={false} Co-authored-by: Mathieu Clerici --- .../mobile/ReactNativeAztec/ReactAztecManager.java | 10 ++++++++++ ios/RNTAztecView/RCTAztecView.swift | 6 ++++++ ios/RNTAztecView/RCTAztecViewManager.m | 2 ++ src/AztecView.js | 1 + 4 files changed, 19 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index dce75ef..ac2fc65 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -6,6 +6,7 @@ import androidx.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; +import android.text.InputType; import android.text.util.Linkify; import android.util.Log; import android.util.TypedValue; @@ -293,6 +294,15 @@ public void setPlaceholderTextColor(ReactAztecText view, @Nullable Integer color } } + @ReactProp(name = "autoCorrect") + public void setAutoCorrect(ReactAztecText view, Boolean autoCorrect) { + if (autoCorrect) { + view.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + } else { + view.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + } + } + @ReactProp(name = "maxImagesWidth") public void setMaxImagesWidth(ReactAztecText view, int maxWidth) { view.setMaxImagesWidth(maxWidth); diff --git a/ios/RNTAztecView/RCTAztecView.swift b/ios/RNTAztecView/RCTAztecView.swift index 1088b14..2bcdcf8 100644 --- a/ios/RNTAztecView/RCTAztecView.swift +++ b/ios/RNTAztecView/RCTAztecView.swift @@ -14,6 +14,7 @@ class RCTAztecView: Aztec.TextView { @objc var onSelectionChange: RCTBubblingEventBlock? = nil @objc var onActiveFormatsChange: RCTBubblingEventBlock? = nil @objc var onActiveFormatAttributesChange: RCTBubblingEventBlock? = nil + @objc var autoCorrect: Bool = false @objc var blockType: NSDictionary? = nil { didSet { guard let block = blockType, let tag = block["tag"] as? String else { @@ -29,6 +30,10 @@ class RCTAztecView: Aztec.TextView { } } + override func didSetProps(_ changedProps: [String]!) { + autocorrectionType = self.autoCorrect ? .yes : .no + } + private var previousContentSize: CGSize = .zero private lazy var placeholderLabel: UILabel = { @@ -66,6 +71,7 @@ class RCTAztecView: Aztec.TextView { placeholderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: contentInset.left + textContainerInset.left + textContainer.lineFragmentPadding), placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: contentInset.top + textContainerInset.top) ]) + autocorrectionType = autoCorrect ? .yes : .no; } // MARK - View Height: Match to content height diff --git a/ios/RNTAztecView/RCTAztecViewManager.m b/ios/RNTAztecView/RCTAztecViewManager.m index f7c733d..6bae310 100644 --- a/ios/RNTAztecView/RCTAztecViewManager.m +++ b/ios/RNTAztecView/RCTAztecViewManager.m @@ -18,6 +18,8 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) + RCT_EXTERN_METHOD(applyFormat:(nonnull NSNumber *)node format:(NSString *)format) RCT_EXTERN_METHOD(setLink:(nonnull NSNumber *)node url:(nonnull NSString *)url title:(nullable NSString *)title) RCT_EXTERN_METHOD(removeLink:(nonnull NSNumber *)node) diff --git a/src/AztecView.js b/src/AztecView.js index f5d1995..3bf6399 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -15,6 +15,7 @@ class AztecView extends React.Component { text: PropTypes.object, placeholder: PropTypes.string, placeholderTextColor: ColorPropType, + autoCorrect: PropTypes.boolean, color: ColorPropType, maxImagesWidth: PropTypes.number, minImagesWidth: PropTypes.number, From a36c3358c076f3809bcd463e113d199fc810c360 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Thu, 13 Jan 2022 15:32:06 -1000 Subject: [PATCH 45/50] feat: adding sendKeysSpaceAndBackspace --- .../mobile/ReactNativeAztec/ReactAztecText.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index f0e7928..095541f 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -10,6 +10,8 @@ import android.text.Spannable; import android.view.Gravity; import android.widget.TextView; +import android.view.inputmethod.BaseInputConnection; +import android.view.KeyEvent; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; @@ -150,9 +152,17 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { super.setSelection(this.length()); showSoftKeyboard(); + + sendKeysSpaceAndBackspace(); return focused; } + public void sendKeysSpaceAndBackspace() { + BaseInputConnection inputConnection = new BaseInputConnection(this, true); + inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)); + inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + } + public void scrollToBottom() { final int scrollAmount = this.getLayout().getLineTop(this.getLineCount()) - this.getHeight() + 50; @@ -453,3 +463,4 @@ public void afterTextChanged(Editable s) { } } } + From 182ab5f12abda295e668318e52ac967448b2009c Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Thu, 13 Jan 2022 16:02:07 -1000 Subject: [PATCH 46/50] chore: updating method name, not calling right on focus as it didn't seem to work --- .../org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 095541f..edeb58d 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -152,12 +152,10 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { super.setSelection(this.length()); showSoftKeyboard(); - - sendKeysSpaceAndBackspace(); return focused; } - public void sendKeysSpaceAndBackspace() { + public void sendSpaceAndBackspace() { BaseInputConnection inputConnection = new BaseInputConnection(this, true); inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)); inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); From 2d0fe6b64cbf5cb0d7dbf0fc977efcd089a88ae4 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Thu, 13 Jan 2022 16:03:31 -1000 Subject: [PATCH 47/50] feat: exposing method to JS for sending space-backspace --- .../mobile/ReactNativeAztec/ReactAztecManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index ac2fc65..f3dc68a 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -55,6 +55,7 @@ public class ReactAztecManager extends SimpleViewManager { private static final int BLUR_TEXT_INPUT = 2; private static final int SET_HTML = 3; private static final int SCROLL_TO_BOTTOM = 4; + private static final int SEND_SPACE_AND_BACKSPACE = 5; private static final int COMMAND_NOTIFY_APPLY_FORMAT = 100; private static final int UNSET = -1; @@ -65,6 +66,7 @@ public class ReactAztecManager extends SimpleViewManager { private int mBlurTextInputCommandCode = BLUR_TEXT_INPUT; // pre-init private int mSetHTMLCommandCode = SET_HTML; private int mScrollToBottomCode = SCROLL_TO_BOTTOM; + private int mSendSpaceAndBackspaceCode = SEND_SPACE_AND_BACKSPACE; private static final String TAG = "ReactAztecText"; @@ -400,6 +402,7 @@ public Map getCommandsMap() { .put("blurTextInput", mBlurTextInputCommandCode) .put("setHTML", mSetHTMLCommandCode) .put("scrollToBottom", mScrollToBottomCode) + .put("sendSpaceAndBackspace", mSendSpaceAndBackspaceCode) .build(); } @@ -424,6 +427,8 @@ public void receiveCommand(final ReactAztecText parent, int commandType, @Nullab } else if (commandType == mScrollToBottomCode) { Log.d("SCROLLING", "1"); parent.scrollToBottom(); + } else if (commandType == mSendSpaceAndBackspaceCode) { + parent.sendSpaceAndBackspace(); } super.receiveCommand(parent, commandType, args); } @@ -585,3 +590,4 @@ public void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) { } } } + From 214e303830868b5e6a385d80abc7a9de989122d8 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 14 Jan 2022 10:45:50 -1000 Subject: [PATCH 48/50] feat: adding sendSpaceAndBackspace method call --- src/AztecView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/AztecView.js b/src/AztecView.js index 3bf6399..88c3678 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -84,6 +84,12 @@ class AztecView extends React.Component { } } + sendSpaceAndBackspace() { + if(Platform.OS === 'android') { + this.dispatch(AztecManager.Commands.sendSpaceAndBackspace); + } + } + requestHTMLWithCursor() { this.dispatch(AztecManager.Commands.returnHTMLWithCursor) } From 2bd3916718dadacca719820c60b39d3bc2d501e2 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 14 Jan 2022 14:50:47 -1000 Subject: [PATCH 49/50] feat: when editor is pressed, call sendSpaceAndBackspace on Android --- src/AztecView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/AztecView.js b/src/AztecView.js index 88c3678..10ca440 100644 --- a/src/AztecView.js +++ b/src/AztecView.js @@ -196,6 +196,12 @@ class AztecView extends React.Component { _onPress = (event) => { this.focus(event); // Call to move the focus in RN way (TextInputState) this._onFocus(event); // Check if there are listeners set on the focus event + + if (Platform.OS === 'android') { + setTimeout(() => { + this.sendSpaceAndBackspace(); + }, 250); + } } render() { From 6b85fe770cb7a164be1874950d72c5054d139f51 Mon Sep 17 00:00:00 2001 From: Elijah Windsor Date: Fri, 14 Jan 2022 16:57:42 -1000 Subject: [PATCH 50/50] feat: updating layout to get it to work right --- .../wordpress/mobile/ReactNativeAztec/ReactAztecManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index f3dc68a..9861696 100644 --- a/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -11,6 +11,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; +import android.view.ViewGroup.LayoutParams; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; @@ -97,6 +98,7 @@ protected ReactAztecText createViewInstance(ThemedReactContext reactContext) { aztecText.setCalypsoMode(false); aztecText.setLinksClickable(true); aztecText.setAutoLinkMask(Linkify.WEB_URLS); + aztecText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); return aztecText; }