Skip to content

Commit

Permalink
Merge pull request #1350 from wordpress-mobile/release/1.19.8
Browse files Browse the repository at this point in the history
Release/1.19.8
  • Loading branch information
twstokes authored Feb 11, 2022
2 parents fc279aa + 3b91198 commit e5735cb
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 6 deletions.
44 changes: 43 additions & 1 deletion Aztec/Classes/TextKit/TextStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ open class TextStorage: NSTextStorage {

private func preprocessAttributesForInsertion(_ attributedString: NSAttributedString) -> NSAttributedString {
let stringWithAttachments = preprocessAttachmentsForInsertion(attributedString)
let preprocessedString = preprocessHeadingsForInsertion(stringWithAttachments)

return stringWithAttachments
return preprocessedString
}

/// Preprocesses an attributed string's attachments for insertion in the storage.
Expand Down Expand Up @@ -211,6 +212,47 @@ open class TextStorage: NSTextStorage {
return finalString
}

/// Preprocesses an attributed string that is missing a `headingRepresentation` attribute for insertion in the storage.
///
/// - Important: This method adds the `headingRepresentation` attribute if it determines the string should contain it.
/// This works around a problem where autocorrected text didn't contain the attribute. This may change in future versions.
///
/// - Parameters:
/// - attributedString: the string we need to preprocess.
///
/// - Returns: the preprocessed string.
///
fileprivate func preprocessHeadingsForInsertion(_ attributedString: NSAttributedString) -> NSAttributedString {
// Ref. https://github.com/wordpress-mobile/AztecEditor-iOS/pull/1334

guard textStore.length > 0, attributedString.length > 0 else {
return attributedString
}

// Get the attributes of the start of the current string in storage.
let currentAttrs = attributes(at: 0, effectiveRange: nil)

guard
// the text currently in storage has a headingRepresentation key
let headerSize = currentAttrs[.headingRepresentation],
// the text coming in doesn't have a headingRepresentation key
attributedString.attribute(.headingRepresentation, at: 0, effectiveRange: nil) == nil,
// the text coming in has a paragraph style attribute
let paragraphStyle = attributedString.attributes(at: 0, effectiveRange: nil)[.paragraphStyle] as? ParagraphStyle,
// the paragraph style contains a property that's a Header type
paragraphStyle.properties.contains(where: { $0 is Header })
else {
// Either the heading attribute wasn't present in the existing string,
// or the attributed string already had it.
return attributedString
}

let processedString = NSMutableAttributedString(attributedString: attributedString)
processedString.addAttribute(.headingRepresentation, value: headerSize, range: attributedString.rangeOfEntireString)

return processedString
}

fileprivate func detectAttachmentRemoved(in range: NSRange) {
// Ref. https://github.com/wordpress-mobile/AztecEditor-iOS/issues/727:
// If the delegate is not set, we *Explicitly* do not want to crash here.
Expand Down
14 changes: 11 additions & 3 deletions Aztec/Classes/TextKit/TextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1111,8 +1111,12 @@ open class TextView: UITextView {
toggle(formatter: formatter, atRange: range)

let liFormatter = LiFormatter(placeholderAttributes: typingAttributes)
toggle(formatter: liFormatter, atRange: range)
let isOlTagPresent = formatter.present(in: storage, at: range)
let isLiTagPresent = liFormatter.present(in: storage, at: range)

if isOlTagPresent != isLiTagPresent {
toggle(formatter: liFormatter, atRange: range)
}
forceRedrawCursorAfterDelay()
}

Expand All @@ -1128,8 +1132,12 @@ open class TextView: UITextView {
toggle(formatter: formatter, atRange: range)

let liFormatter = LiFormatter(placeholderAttributes: typingAttributes)
toggle(formatter: liFormatter, atRange: range)

let isOlTagPresent = formatter.present(in: storage, at: range)
let isLiTagPresent = liFormatter.present(in: storage, at: range)

if isOlTagPresent != isLiTagPresent {
toggle(formatter: liFormatter, atRange: range)
}
forceRedrawCursorAfterDelay()
}

Expand Down
54 changes: 54 additions & 0 deletions AztecTests/TextKit/TextStorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -579,4 +579,58 @@ class TextStorageTests: XCTestCase {
let result = storage.getHTML()
XCTAssertEqual(expectedResult, result)
}

/// Verifies that missing Heading attributes are retained on string replacements when appropriate
///
func testMissingHeadingAttributeIsRetained() {
let formatter = HeaderFormatter(headerLevel: .h2)
storage.replaceCharacters(in: storage.rangeOfEntireString, with: "Hello i'm a header")
formatter.applyAttributes(to: storage, at: storage.rangeOfEntireString)

let originalAttributes = storage.attributes(at: 0, effectiveRange: nil)
XCTAssertEqual(storage.string, "Hello i'm a header")
XCTAssertEqual(originalAttributes.count, 3)
XCTAssertNotNil(originalAttributes[.headingRepresentation])

let autoCorrectedAttributes = originalAttributes.filter { $0.key != .headingRepresentation }

let autoCorrectedString = NSAttributedString(
string: "I'm",
attributes: autoCorrectedAttributes
)

let range = NSRange(location: 6, length: 3)
storage.replaceCharacters(in: range, with: autoCorrectedString)

let finalAttributes = storage.attributes(at: range.location, effectiveRange: nil)
XCTAssertEqual(storage.string, "Hello I'm a header")
XCTAssertEqual(originalAttributes.keys, finalAttributes.keys)
}

/// Verifies that converting a Heading to a Paragraph doesn't retain the heading attribute
///
func testHeadingToParagraphDoesNotRetainHeadingAttribute() {
let headerFormatter = HeaderFormatter(headerLevel: .h2)
storage.replaceCharacters(in: storage.rangeOfEntireString, with: "Hello I'm a header")
headerFormatter.applyAttributes(to: storage, at: storage.rangeOfEntireString)

let originalAttributes = storage.attributes(at: 0, effectiveRange: nil)
XCTAssertEqual(storage.string, "Hello I'm a header")
XCTAssertNotNil(originalAttributes[.headingRepresentation])

let paragraphAttributes: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 14),
.paragraphStyle: ParagraphStyle.default
]

let paragraphString = NSAttributedString(
string: "Hello I'm a paragraph",
attributes: paragraphAttributes
)
storage.replaceCharacters(in: storage.rangeOfEntireString, with: paragraphString)

let finalAttributes = storage.attributes(at: 0, effectiveRange: nil)
XCTAssertEqual(storage.string, "Hello I'm a paragraph")
XCTAssertNil(finalAttributes[.headingRepresentation])
}
}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
1.19.8
-------
* Fix Li tag when switching the list style.
* Retain Heading attribute when headings are autocorrected.

1.19.7
-------
* Add variable to control whether typing attributes should be recalculated when deleting backward.
Expand Down
2 changes: 1 addition & 1 deletion WordPress-Aztec-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'WordPress-Aztec-iOS'
s.version = '1.19.7'
s.version = '1.19.8'

s.summary = 'The native HTML Editor.'
s.description = <<-DESC
Expand Down
2 changes: 1 addition & 1 deletion WordPress-Editor-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'WordPress-Editor-iOS'
s.version = '1.19.7'
s.version = '1.19.8'

s.summary = 'The WordPress HTML Editor.'
s.description = <<-DESC
Expand Down

0 comments on commit e5735cb

Please sign in to comment.