Skip to content
This repository was archived by the owner on Mar 7, 2025. It is now read-only.
Closed
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
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ buildscript {
wordpressUtilsVersion = '1.22'
espressoVersion = '3.0.1'

aztecVersion = 'v1.3.14'
aztecVersion = 'v1.3.16'
}

repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@
import org.wordpress.aztec.plugins.wpcomments.toolbar.MoreToolbarButton;

import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;

public class ReactAztecManager extends SimpleViewManager<ReactAztecText> {

public static final String REACT_CLASS = "RCTAztecView";

private static final int FOCUS_TEXT_INPUT = 1;
private static final int BLUR_TEXT_INPUT = 2;
private static final int COMMAND_NOTIFY_APPLY_FORMAT = 100;

// we define the same codes in ReactAztecText as they have for ReactNative's TextInput, so
// it's easier to handle focus between Aztec and TextInput instances on the same screen.
Expand Down Expand Up @@ -171,6 +172,19 @@ private void setTextfromJS(ReactAztecText view, String text) {
view.setIsSettingTextFromJS(false);
}

@ReactProp(name = "activeFormats", defaultBoolean = false)
public void setActiveFormats(final ReactAztecText view, @Nullable ReadableArray activeFormats) {
if (activeFormats != null) {
String[] activeFormatsArray = new String[activeFormats.size()];
for (int i = 0; i < activeFormats.size(); i++) {
activeFormatsArray[i] = activeFormats.getString(i);
}
view.setActiveFormats(Arrays.asList(activeFormatsArray));
} else {
view.setActiveFormats(new ArrayList<String>());
}
}

@ReactProp(name = "color", customType = "Color")
public void setColor(ReactAztecText view, @Nullable Integer color) {
int newColor = Color.BLACK;
Expand Down Expand Up @@ -254,11 +268,6 @@ public void setOnContentSizeChange(final ReactAztecText view, boolean onContentS
}
}

@ReactProp(name = "onActiveFormatsChange", defaultBoolean = false)
public void setOnActiveFormatsChange(final ReactAztecText view, boolean onActiveFormatsChange) {
view.shouldHandleActiveFormatsChange = onActiveFormatsChange;
}

@ReactProp(name = "onSelectionChange", defaultBoolean = false)
public void setOnSelectionChange(final ReactAztecText view, boolean onSelectionChange) {
view.shouldHandleOnSelectionChange = onSelectionChange;
Expand Down Expand Up @@ -286,7 +295,6 @@ public void setOnBackspaceHandling(final ReactAztecText view, boolean onBackspac
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.<String, Integer>builder()
.put("applyFormat", COMMAND_NOTIFY_APPLY_FORMAT)
.put("focusTextInput", mFocusTextInputCommandCode)
.put("blurTextInput", mBlurTextInputCommandCode)
.build();
Expand All @@ -295,12 +303,7 @@ public Map<String, Integer> getCommandsMap() {
@Override
public void receiveCommand(final ReactAztecText parent, int commandType, @Nullable ReadableArray args) {
Assertions.assertNotNull(parent);
if (commandType == COMMAND_NOTIFY_APPLY_FORMAT) {
final String format = args.getString(0);
Log.d(TAG, String.format("Apply format: %s", format));
parent.applyFormat(format);
return;
} else if (commandType == mFocusTextInputCommandCode) {
if (commandType == mFocusTextInputCommandCode) {
parent.requestFocusFromJS();
return;
} else if (commandType == mBlurTextInputCommandCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;

public class ReactAztecText extends AztecText {

Expand All @@ -52,6 +56,16 @@ public class ReactAztecText extends AztecText {
boolean shouldHandleOnSelectionChange = false;
boolean shouldHandleActiveFormatsChange = false;

private static final HashMap<ITextFormat, String> typingFormatsMap = new HashMap<ITextFormat, String>() {
{
put(AztecTextFormat.FORMAT_BOLD, "bold");
put(AztecTextFormat.FORMAT_STRONG, "bold");
put(AztecTextFormat.FORMAT_ITALIC, "italic");
put(AztecTextFormat.FORMAT_CITE, "italic");
put(AztecTextFormat.FORMAT_STRIKETHROUGH, "bold");
}
};

public ReactAztecText(ThemedReactContext reactContext) {
super(reactContext);
this.setAztecKeyListener(new ReactAztecText.OnAztecKeyListener() {
Expand Down Expand Up @@ -316,66 +330,27 @@ private boolean onBackspace() {
return true;
}

public void applyFormat(String format) {
ArrayList<ITextFormat> newFormats = new ArrayList<>();
switch (format) {
case ("bold"):
case ("strong"):
newFormats.add(AztecTextFormat.FORMAT_STRONG);
newFormats.add(AztecTextFormat.FORMAT_BOLD);
break;
case ("italic"):
newFormats.add(AztecTextFormat.FORMAT_ITALIC);
newFormats.add(AztecTextFormat.FORMAT_CITE);
break;
case ("strikethrough"):
newFormats.add(AztecTextFormat.FORMAT_STRIKETHROUGH);
break;
}

if (newFormats.size() == 0) {
return;
}

if (!isTextSelected()) {
final ArrayList<ITextFormat> newStylesList = getNewStylesList(newFormats);
setSelectedStyles(newStylesList);
// Update the toolbar state
updateToolbarButtons(newStylesList);
} else {
toggleFormatting(newFormats.get(0));
// Update the toolbar state
updateToolbarButtons(getSelectionStart(), getSelectionEnd());
}

// emit onChange because the underlying HTML has changed applying the style
ReactContext reactContext = (ReactContext) getContext();
EventDispatcher eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
eventDispatcher.dispatchEvent(
new ReactTextChangedEvent(
getId(),
toHtml(false),
incrementAndGetEventCounter())
);
}

// Removes all formats in the list but if none found, applies the first one
private ArrayList<ITextFormat> getNewStylesList(ArrayList<ITextFormat> newFormats) {
ArrayList<ITextFormat> textFormats = new ArrayList<>();
textFormats.addAll(getSelectedStyles());
boolean wasRemoved = false;
for (ITextFormat newFormat : newFormats) {
if (textFormats.contains(newFormat)) {
wasRemoved = true;
textFormats.remove(newFormat);
public void setActiveFormats(Iterable<String> newFormats) {
Set<ITextFormat> selectedStylesSet = new HashSet<>(getSelectedStyles());
Set<ITextFormat> newFormatsSet = new HashSet<>();
for (String newFormat : newFormats) {
switch (newFormat) {
case "bold":
newFormatsSet.add(AztecTextFormat.FORMAT_STRONG);
break;
case "italic":
newFormatsSet.add(AztecTextFormat.FORMAT_CITE);
break;
case "strikethrough":
newFormatsSet.add(AztecTextFormat.FORMAT_STRIKETHROUGH);
break;
}
}

if (!wasRemoved) {
textFormats.add(newFormats.get(0));
}

return textFormats;
selectedStylesSet.removeAll(typingFormatsMap.keySet());
selectedStylesSet.addAll(newFormatsSet);
ArrayList<ITextFormat> newStylesList = new ArrayList<>(selectedStylesSet);
setSelectedStyles(newStylesList);
updateToolbarButtons(newStylesList);
}

/**
Expand Down
72 changes: 13 additions & 59 deletions ios/RNTAztecView/RCTAztecView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ class RCTAztecView: Aztec.TextView {
@objc var onBlur: RCTBubblingEventBlock? = nil
@objc var onContentSizeChange: RCTBubblingEventBlock? = nil
@objc var onSelectionChange: RCTBubblingEventBlock? = nil
@objc var onActiveFormatsChange: RCTBubblingEventBlock? = nil
@objc var onActiveFormatAttributesChange: RCTBubblingEventBlock? = nil
@objc var blockType: NSDictionary? = nil {
didSet {
guard let block = blockType, let tag = block["tag"] as? String else {
Expand All @@ -20,6 +18,14 @@ class RCTAztecView: Aztec.TextView {
blockModel = BlockModel(tag: tag)
}
}
@objc var activeFormats: NSSet? = nil {
didSet {
let currentTypingAttributes = formattingIdentifiersForTypingAttributes()
for (key, value) in formatStringMap where currentTypingAttributes.contains(key) != activeFormats?.contains(value) {
Copy link
Member

@koke koke Dec 21, 2018

Choose a reason for hiding this comment

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

It's a bit hard to follow but it seems this is adding any missing format to the typing attributes, but should it also be removing anything that's currently there and not in activeFormats?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's what it does, see apply() actually toggles a given format. It's a bit unfortunate that I didn't find any public method in Aztec to force set a format instead

toggleFormat(format: value)
}
}
}

var blockModel = BlockModel(tag: "") {
didSet {
Expand Down Expand Up @@ -206,44 +212,16 @@ class RCTAztecView: Aztec.TextView {

// MARK: - Formatting interface

@objc func apply(format: String) {
@objc func toggleFormat(format: String) {
let emptyRange = NSRange(location: selectedRange.location, length: 0)
switch format {
case "bold": toggleBold(range: selectedRange)
case "italic": toggleItalic(range: selectedRange)
case "strikethrough": toggleStrikethrough(range: selectedRange)
case "bold": toggleBold(range: emptyRange)
case "italic": toggleItalic(range: emptyRange)
case "strikethrough": toggleStrikethrough(range: emptyRange)
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
}

func forceTypingAttributesIfNeeded() {
if let formatHandler = HeadingBlockFormatHandler(block: blockModel) {
formatHandler.forceTypingFormat(on: self)
Expand All @@ -259,27 +237,6 @@ class RCTAztecView: Aztec.TextView {
}
}

func propagateFormatChanges() {
guard let onActiveFormatsChange = onActiveFormatsChange else {
return
}
let identifiers: Set<FormattingIdentifier>
if selectedRange.length > 0 {
identifiers = formattingIdentifiersSpanningRange(selectedRange)
} else {
identifiers = formattingIdentifiersForTypingAttributes()
}
let formats = identifiers.compactMap { formatStringMap[$0] }
onActiveFormatsChange(["formats": formats])
}

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

func propagateSelectionChanges() {
guard let onSelectionChange = onSelectionChange else {
return
Expand All @@ -293,14 +250,11 @@ class RCTAztecView: Aztec.TextView {
extension RCTAztecView: UITextViewDelegate {

func textViewDidChangeSelection(_ textView: UITextView) {
propagateAttributesChanges()
propagateFormatChanges()
propagateSelectionChanges()
}

func textViewDidChange(_ textView: UITextView) {
forceTypingAttributesIfNeeded()
propagateFormatChanges()
propagateContentChanges()
}

Expand Down
1 change: 1 addition & 0 deletions ios/RNTAztecView/RCTAztecViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject)
RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(blockType, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(activeFormats, NSSet)

RCT_EXPORT_VIEW_PROPERTY(onActiveFormatsChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onActiveFormatAttributesChange, RCTBubblingEventBlock)
Expand Down
21 changes: 0 additions & 21 deletions ios/RNTAztecView/RCTAztecViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,6 @@ public class RCTAztecViewManager: RCTViewManager {
return true
}

@objc
func applyFormat(_ node: NSNumber, format: String) {
executeBlock({ (aztecView) in
aztecView.apply(format: format)
}, 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
Loading