Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding set/remove link methods. #87

Merged
merged 3 commits into from
Dec 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions ios/RNTAztecView/RCTAztecView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ class RCTAztecView: Aztec.TextView {
@objc var onContentSizeChange: RCTBubblingEventBlock? = nil
@objc var onSelectionChange: RCTBubblingEventBlock? = nil
@objc var onActiveFormatsChange: RCTBubblingEventBlock? = nil

@objc var onActiveFormatAttributesChange: RCTBubblingEventBlock? = nil

private var previousContentSize: CGSize = .zero

private lazy var placeholderLabel: UILabel = {
let label = UILabel(frame: .zero)
return label
}()

private let formatStringMap: [FormattingIdentifier: String] = [
.bold: "Bold",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial character in the string here is capitalized, but not for the others. I'd suggest to standardize for clarity.

.italic: "italic",
.strikethrough: "strikethrough",
.link: "link",
]

override init(defaultFont: UIFont, defaultParagraphStyle: ParagraphStyle, defaultMissingImage: UIImage) {
super.init(defaultFont: defaultFont, defaultParagraphStyle: defaultParagraphStyle, defaultMissingImage: defaultMissingImage)
commonInit()
Expand Down Expand Up @@ -182,6 +190,35 @@ class RCTAztecView: Aztec.TextView {
default: print("Format not recognized")
}
}

@objc
func setLink(with url: String, and title: String?) {
guard let url = URL(string: url) else {
return
}
if let title = title {
setLink(url, title: title, inRange: selectedRange)
} else {
setLink(url, inRange: selectedRange)
}
}

@objc
func removeLink() {
guard let expandedRange = linkFullRange(forRange: selectedRange) else {
return
}
removeLink(inRange: expandedRange)
}

func linkAttributes() -> [String: Any] {
var attributes: [String: Any] = ["isActive": false]
if let expandedRange = linkFullRange(forRange: selectedRange) {
attributes["url"] = linkURL(forRange: expandedRange)?.absoluteString ?? ""
attributes["isActive"] = true
}
return attributes
}

// MARK: - Event Propagation

Expand All @@ -202,17 +239,21 @@ class RCTAztecView: Aztec.TextView {
} else {
identifiers = formatIdentifiersForTypingAttributes()
}
let formats = identifiers.compactMap( { (identifier) -> String? in
switch identifier {
case .bold: return "bold"
case .italic: return "italic"
case .strikethrough: return "strikethrough"
default: return nil
}
})
let formats = identifiers.compactMap(formatString)
onActiveFormatsChange(["formats": formats])
}

func propagateAttributesChanges() {
let attributes: [String: [String: Any]] = [
"link": linkAttributes()
]
onActiveFormatAttributesChange?(["attributes": attributes])
}

private func formatString(from identifier: FormattingIdentifier) -> String? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that:

  1. This method is only being used in the compactMap() call above as far as I can tell.
  2. This method is effectively equivalent to formatStringMap[identifier] if we need the format string from any other place in our code.

I'm wondering if we really need this to be a method and not just a closure, such as { formatStringMap[$0] } above (untested but you get the idea).

return formatStringMap[identifier]
}

func propagateSelectionChanges() {
guard let onSelectionChange = onSelectionChange else {
return
Expand All @@ -226,6 +267,7 @@ class RCTAztecView: Aztec.TextView {
extension RCTAztecView: UITextViewDelegate {

func textViewDidChangeSelection(_ textView: UITextView) {
propagateAttributesChanges()
propagateFormatChanges()
propagateSelectionChanges()
}
Expand Down
3 changes: 3 additions & 0 deletions ios/RNTAztecView/RCTAztecViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject)
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onActiveFormatsChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onActiveFormatAttributesChange, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor)

RCT_EXTERN_METHOD(applyFormat:(nonnull NSNumber *)node format:(NSString *)format)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could use applyFormat instead of setLink and removeLink to set and remove a link?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right, applyFormat only accepts a string (the format) as parameter, I guess we need something else for the url 👍

RCT_EXTERN_METHOD(setLink:(nonnull NSNumber *)node url:(nonnull NSString *)url title:(nullable NSString *)title)
RCT_EXTERN_METHOD(removeLink:(nonnull NSNumber *)node)

@end
14 changes: 14 additions & 0 deletions ios/RNTAztecView/RCTAztecViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ public class RCTAztecViewManager: RCTViewManager {
}, onNode: node)
}

@objc
func removeLink(_ node: NSNumber) {
executeBlock({ (aztecView) in
aztecView.removeLink()
}, onNode: node)
}

@objc
func setLink(_ node: NSNumber, url: String, title: String?) {
executeBlock({ (aztecView) in
aztecView.setLink(with: url, and: title)
}, onNode: node)
}

@objc
public override func view() -> UIView {
let view = RCTAztecView(
Expand Down
40 changes: 31 additions & 9 deletions src/AztecView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react';
import ReactNative, {requireNativeComponent, ViewPropTypes, UIManager, ColorPropType, TouchableWithoutFeedback} from 'react-native';
import TextInputState from 'react-native/lib/TextInputState';

const AztecManager = UIManager.RCTAztecView;

class AztecView extends React.Component {

static propTypes = {
Expand All @@ -22,25 +24,35 @@ class AztecView extends React.Component {
onBackspace: PropTypes.func,
onScroll: PropTypes.func,
onActiveFormatsChange: PropTypes.func,
onActiveFormatAttributesChange: PropTypes.func,
onSelectionChange: PropTypes.func,
onHTMLContentWithCursor: PropTypes.func,
...ViewPropTypes, // include the default view properties
}

applyFormat(format) {
dispatch(command, params) {
params = params || [];
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(this),
UIManager.RCTAztecView.Commands.applyFormat,
[format],
);
command,
params,
);
}

applyFormat(format) {
this.dispatch(AztecManager.Commands.applyFormat, [format])
}

removeLink() {
this.dispatch(AztecManager.Commands.removeLink)
}

setLink(url, title) {
this.dispatch(AztecManager.Commands.setLink, [url, title])
}

requestHTMLWithCursor() {
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(this),
UIManager.RCTAztecView.Commands.returnHTMLWithCursor,
[],
);
this.dispatch(AztecManager.Commands.returnHTMLWithCursor)
}

_onActiveFormatsChange = (event) => {
Expand All @@ -52,6 +64,15 @@ class AztecView extends React.Component {
onActiveFormatsChange(formats);
}

_onActiveFormatAttributesChange = (event) => {
if (!this.props.onActiveFormatAttributesChange) {
return;
}
const attributes = event.nativeEvent.attributes;
const { onActiveFormatAttributesChange } = this.props;
onActiveFormatAttributesChange(attributes);
}

_onContentSizeChange = (event) => {
if (!this.props.onContentSizeChange) {
return;
Expand Down Expand Up @@ -134,6 +155,7 @@ class AztecView extends React.Component {
<TouchableWithoutFeedback onPress={ this._onPress }>
<RCTAztecView {...otherProps}
onActiveFormatsChange={ this._onActiveFormatsChange }
onActiveFormatAttributesChange={ this._onActiveFormatAttributesChange }
onContentSizeChange = { this._onContentSizeChange }
onHTMLContentWithCursor = { this._onHTMLContentWithCursor }
onSelectionChange = { this._onSelectionChange }
Expand Down