Skip to content

Commit e5735cb

Browse files
authored
Merge pull request #1350 from wordpress-mobile/release/1.19.8
Release/1.19.8
2 parents fc279aa + 3b91198 commit e5735cb

File tree

6 files changed

+115
-6
lines changed

6 files changed

+115
-6
lines changed

Aztec/Classes/TextKit/TextStorage.swift

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ open class TextStorage: NSTextStorage {
145145

146146
private func preprocessAttributesForInsertion(_ attributedString: NSAttributedString) -> NSAttributedString {
147147
let stringWithAttachments = preprocessAttachmentsForInsertion(attributedString)
148+
let preprocessedString = preprocessHeadingsForInsertion(stringWithAttachments)
148149

149-
return stringWithAttachments
150+
return preprocessedString
150151
}
151152

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

215+
/// Preprocesses an attributed string that is missing a `headingRepresentation` attribute for insertion in the storage.
216+
///
217+
/// - Important: This method adds the `headingRepresentation` attribute if it determines the string should contain it.
218+
/// This works around a problem where autocorrected text didn't contain the attribute. This may change in future versions.
219+
///
220+
/// - Parameters:
221+
/// - attributedString: the string we need to preprocess.
222+
///
223+
/// - Returns: the preprocessed string.
224+
///
225+
fileprivate func preprocessHeadingsForInsertion(_ attributedString: NSAttributedString) -> NSAttributedString {
226+
// Ref. https://github.com/wordpress-mobile/AztecEditor-iOS/pull/1334
227+
228+
guard textStore.length > 0, attributedString.length > 0 else {
229+
return attributedString
230+
}
231+
232+
// Get the attributes of the start of the current string in storage.
233+
let currentAttrs = attributes(at: 0, effectiveRange: nil)
234+
235+
guard
236+
// the text currently in storage has a headingRepresentation key
237+
let headerSize = currentAttrs[.headingRepresentation],
238+
// the text coming in doesn't have a headingRepresentation key
239+
attributedString.attribute(.headingRepresentation, at: 0, effectiveRange: nil) == nil,
240+
// the text coming in has a paragraph style attribute
241+
let paragraphStyle = attributedString.attributes(at: 0, effectiveRange: nil)[.paragraphStyle] as? ParagraphStyle,
242+
// the paragraph style contains a property that's a Header type
243+
paragraphStyle.properties.contains(where: { $0 is Header })
244+
else {
245+
// Either the heading attribute wasn't present in the existing string,
246+
// or the attributed string already had it.
247+
return attributedString
248+
}
249+
250+
let processedString = NSMutableAttributedString(attributedString: attributedString)
251+
processedString.addAttribute(.headingRepresentation, value: headerSize, range: attributedString.rangeOfEntireString)
252+
253+
return processedString
254+
}
255+
214256
fileprivate func detectAttachmentRemoved(in range: NSRange) {
215257
// Ref. https://github.com/wordpress-mobile/AztecEditor-iOS/issues/727:
216258
// If the delegate is not set, we *Explicitly* do not want to crash here.

Aztec/Classes/TextKit/TextView.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,8 +1111,12 @@ open class TextView: UITextView {
11111111
toggle(formatter: formatter, atRange: range)
11121112

11131113
let liFormatter = LiFormatter(placeholderAttributes: typingAttributes)
1114-
toggle(formatter: liFormatter, atRange: range)
1114+
let isOlTagPresent = formatter.present(in: storage, at: range)
1115+
let isLiTagPresent = liFormatter.present(in: storage, at: range)
11151116

1117+
if isOlTagPresent != isLiTagPresent {
1118+
toggle(formatter: liFormatter, atRange: range)
1119+
}
11161120
forceRedrawCursorAfterDelay()
11171121
}
11181122

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

11301134
let liFormatter = LiFormatter(placeholderAttributes: typingAttributes)
1131-
toggle(formatter: liFormatter, atRange: range)
1132-
1135+
let isOlTagPresent = formatter.present(in: storage, at: range)
1136+
let isLiTagPresent = liFormatter.present(in: storage, at: range)
1137+
1138+
if isOlTagPresent != isLiTagPresent {
1139+
toggle(formatter: liFormatter, atRange: range)
1140+
}
11331141
forceRedrawCursorAfterDelay()
11341142
}
11351143

AztecTests/TextKit/TextStorageTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,58 @@ class TextStorageTests: XCTestCase {
579579
let result = storage.getHTML()
580580
XCTAssertEqual(expectedResult, result)
581581
}
582+
583+
/// Verifies that missing Heading attributes are retained on string replacements when appropriate
584+
///
585+
func testMissingHeadingAttributeIsRetained() {
586+
let formatter = HeaderFormatter(headerLevel: .h2)
587+
storage.replaceCharacters(in: storage.rangeOfEntireString, with: "Hello i'm a header")
588+
formatter.applyAttributes(to: storage, at: storage.rangeOfEntireString)
589+
590+
let originalAttributes = storage.attributes(at: 0, effectiveRange: nil)
591+
XCTAssertEqual(storage.string, "Hello i'm a header")
592+
XCTAssertEqual(originalAttributes.count, 3)
593+
XCTAssertNotNil(originalAttributes[.headingRepresentation])
594+
595+
let autoCorrectedAttributes = originalAttributes.filter { $0.key != .headingRepresentation }
596+
597+
let autoCorrectedString = NSAttributedString(
598+
string: "I'm",
599+
attributes: autoCorrectedAttributes
600+
)
601+
602+
let range = NSRange(location: 6, length: 3)
603+
storage.replaceCharacters(in: range, with: autoCorrectedString)
604+
605+
let finalAttributes = storage.attributes(at: range.location, effectiveRange: nil)
606+
XCTAssertEqual(storage.string, "Hello I'm a header")
607+
XCTAssertEqual(originalAttributes.keys, finalAttributes.keys)
608+
}
609+
610+
/// Verifies that converting a Heading to a Paragraph doesn't retain the heading attribute
611+
///
612+
func testHeadingToParagraphDoesNotRetainHeadingAttribute() {
613+
let headerFormatter = HeaderFormatter(headerLevel: .h2)
614+
storage.replaceCharacters(in: storage.rangeOfEntireString, with: "Hello I'm a header")
615+
headerFormatter.applyAttributes(to: storage, at: storage.rangeOfEntireString)
616+
617+
let originalAttributes = storage.attributes(at: 0, effectiveRange: nil)
618+
XCTAssertEqual(storage.string, "Hello I'm a header")
619+
XCTAssertNotNil(originalAttributes[.headingRepresentation])
620+
621+
let paragraphAttributes: [NSAttributedString.Key: Any] = [
622+
.font: UIFont.systemFont(ofSize: 14),
623+
.paragraphStyle: ParagraphStyle.default
624+
]
625+
626+
let paragraphString = NSAttributedString(
627+
string: "Hello I'm a paragraph",
628+
attributes: paragraphAttributes
629+
)
630+
storage.replaceCharacters(in: storage.rangeOfEntireString, with: paragraphString)
631+
632+
let finalAttributes = storage.attributes(at: 0, effectiveRange: nil)
633+
XCTAssertEqual(storage.string, "Hello I'm a paragraph")
634+
XCTAssertNil(finalAttributes[.headingRepresentation])
635+
}
582636
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.19.8
2+
-------
3+
* Fix Li tag when switching the list style.
4+
* Retain Heading attribute when headings are autocorrected.
5+
16
1.19.7
27
-------
38
* Add variable to control whether typing attributes should be recalculated when deleting backward.

WordPress-Aztec-iOS.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'WordPress-Aztec-iOS'
3-
s.version = '1.19.7'
3+
s.version = '1.19.8'
44

55
s.summary = 'The native HTML Editor.'
66
s.description = <<-DESC

WordPress-Editor-iOS.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'WordPress-Editor-iOS'
3-
s.version = '1.19.7'
3+
s.version = '1.19.8'
44

55
s.summary = 'The WordPress HTML Editor.'
66
s.description = <<-DESC

0 commit comments

Comments
 (0)