diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 5e05dc1e..166a173b 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -2451,6 +2451,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = TuskerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2804,6 +2805,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = TuskerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2823,6 +2825,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = TuskerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Tusker/Extensions/AttributedString+Helpers.swift b/Tusker/Extensions/AttributedString+Helpers.swift index 9c244695..5dd038ba 100644 --- a/Tusker/Extensions/AttributedString+Helpers.swift +++ b/Tusker/Extensions/AttributedString+Helpers.swift @@ -30,20 +30,31 @@ extension NSAttributedString { extension NSMutableAttributedString { func trimLeadingCharactersInSet(_ charSet: CharacterSet) { - var range = (string as NSString).rangeOfCharacter(from: charSet) - - while range.length != 0 && range.location == 0 { - replaceCharacters(in: range, with: "") - range = (string as NSString).rangeOfCharacter(from: charSet) + var end = string.startIndex + while end < string.endIndex && charSet.contains(string.unicodeScalars[end]) { + end = string.unicodeScalars.index(after: end) + } + if end > string.startIndex { + let length = string.utf16.distance(from: string.startIndex, to: end) + replaceCharacters(in: NSRange(location: 0, length: length), with: "") } } func trimTrailingCharactersInSet(_ charSet: CharacterSet) { - var range = (string as NSString).rangeOfCharacter(from: charSet, options: .backwards) - - while range.length != 0 && range.length + range.location == length { - replaceCharacters(in: range, with: "") - range = (string as NSString).rangeOfCharacter(from: charSet, options: .backwards) + if string.isEmpty { + return + } + var start = string.index(before: string.endIndex) + while start > string.startIndex && charSet.contains(string.unicodeScalars[start]) { + start = string.unicodeScalars.index(before: start) + } + if start < string.endIndex { + if start != string.startIndex || !charSet.contains(string.unicodeScalars[start]) { + start = string.unicodeScalars.index(after: start) + } + let location = string.utf16.distance(from: string.startIndex, to: start) + let length = string.utf16.distance(from: start, to: string.endIndex) + replaceCharacters(in: NSRange(location: location, length: length), with: "") } } diff --git a/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift b/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift index 0ad6e702..a9e98680 100644 --- a/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift @@ -388,7 +388,11 @@ class ConversationMainStatusCollectionViewCell: UICollectionViewListCell, Status let html = translation?.content ?? status.content let attributedContent = ConversationMainStatusCollectionViewCell.htmlConverter.convert(html) - doUpdateUI(status: status, content: attributedContent) + let collapsedContent = NSMutableAttributedString(attributedString: attributedContent) + collapsedContent.collapseWhitespace() + collapsedContent.trimLeadingCharactersInSet(.whitespacesAndNewlines) + collapsedContent.trimTrailingCharactersInSet(.whitespacesAndNewlines) + doUpdateUI(status: status, content: collapsedContent) if !status.spoilerText.isEmpty, let translated = translation?.spoilerText { diff --git a/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift b/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift index a84dd447..d99c3c98 100644 --- a/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift +++ b/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift @@ -616,7 +616,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti } let content = precomputedContent ?? TimelineStatusCollectionViewCell.htmlConverter.convert(status.content) - doUpdateUI(status: status, content: content) + let collapsedContent = NSMutableAttributedString(attributedString: content) + collapsedContent.collapseWhitespace() + collapsedContent.trimLeadingCharactersInSet(.whitespacesAndNewlines) + collapsedContent.trimTrailingCharactersInSet(.whitespacesAndNewlines) + doUpdateUI(status: status, content: collapsedContent) doUpdateTimestamp(status: status) timestampLabel.isHidden = showPinned diff --git a/TuskerTests/AttributedStringHelperTests.swift b/TuskerTests/AttributedStringHelperTests.swift index 5def7946..e3639150 100644 --- a/TuskerTests/AttributedStringHelperTests.swift +++ b/TuskerTests/AttributedStringHelperTests.swift @@ -16,6 +16,36 @@ class AttributedStringHelperTests: XCTestCase { override func tearDown() { } + + func testTrimLeading() { + let a = NSMutableAttributedString(string: " a ") + a.trimLeadingCharactersInSet(.whitespaces) + XCTAssertEqual(a, NSAttributedString(string: "a ")) + let b = NSMutableAttributedString(string: " ") + b.trimLeadingCharactersInSet(.whitespaces) + XCTAssertEqual(b, NSAttributedString(string: "")) + let c = NSMutableAttributedString(string: "") + c.trimLeadingCharactersInSet(.whitespaces) + XCTAssertEqual(c, NSAttributedString(string: "")) + let d = NSMutableAttributedString(string: "abc") + d.trimLeadingCharactersInSet(.whitespaces) + XCTAssertEqual(d, NSAttributedString(string: "abc")) + } + + func testTrimTrailing() { + let a = NSMutableAttributedString(string: " a ") + a.trimTrailingCharactersInSet(.whitespaces) + XCTAssertEqual(a, NSAttributedString(string: " a")) + let b = NSMutableAttributedString(string: " ") + b.trimTrailingCharactersInSet(.whitespaces) + XCTAssertEqual(b, NSAttributedString(string: "")) + let c = NSMutableAttributedString(string: "") + c.trimTrailingCharactersInSet(.whitespaces) + XCTAssertEqual(c, NSAttributedString(string: "")) + let d = NSMutableAttributedString(string: "abc") + d.trimTrailingCharactersInSet(.whitespaces) + XCTAssertEqual(d, NSAttributedString(string: "abc")) + } func testCollapsingWhitespace() { var str = NSAttributedString(string: "test 1\n")