diff --git a/Modules/Package.swift b/Modules/Package.swift index 9d07d6b80bb7..4fbf4c4122d6 100644 --- a/Modules/Package.swift +++ b/Modules/Package.swift @@ -318,9 +318,7 @@ let package = Package( "FormattableContentKit", "SFHFKeychainUtils", "WordPressShared", - "WordPressSharedUI", "WordPressKit", - "WordPressUI", .product(name: "CocoaLumberjack", package: "CocoaLumberjack"), .product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"), .product(name: "Gravatar", package: "Gravatar-SDK-iOS"), diff --git a/Modules/Sources/WordPressData/Mapping/ReaderPost+Mapping.swift b/Modules/Sources/WordPressData/Mapping/ReaderPost+Mapping.swift index 0dc60ac81fa8..8c7e26e41a6b 100644 --- a/Modules/Sources/WordPressData/Mapping/ReaderPost+Mapping.swift +++ b/Modules/Sources/WordPressData/Mapping/ReaderPost+Mapping.swift @@ -1,7 +1,6 @@ import CoreData import WordPressKit import WordPressShared -import WordPressSharedUI extension ReaderPost { /// Finds an existing `ReaderPost` matching the given `globalID` and `topic`, diff --git a/Modules/Sources/WordPressData/Swift/ManagedPerson.swift b/Modules/Sources/WordPressData/Swift/ManagedPerson.swift index aea4c9e0e328..08d8cfa95220 100644 --- a/Modules/Sources/WordPressData/Swift/ManagedPerson.swift +++ b/Modules/Sources/WordPressData/Swift/ManagedPerson.swift @@ -1,7 +1,6 @@ import Foundation import CoreData import WordPressKit -import WordPressUI import Gravatar public typealias Person = RemotePerson diff --git a/Modules/Sources/WordPressSharedUI/RichContentFormatter.swift b/Modules/Sources/WordPressShared/Utility/RichContentFormatter.swift similarity index 78% rename from Modules/Sources/WordPressSharedUI/RichContentFormatter.swift rename to Modules/Sources/WordPressShared/Utility/RichContentFormatter.swift index b9cfc55e7cb0..38530a2d2043 100644 --- a/Modules/Sources/WordPressSharedUI/RichContentFormatter.swift +++ b/Modules/Sources/WordPressShared/Utility/RichContentFormatter.swift @@ -1,7 +1,4 @@ import Foundation -import UIKit -import WordPressShared -import WordPressSharedObjCUI /// Contains methods for formatting post or comment content for display. /// @@ -28,7 +25,7 @@ import WordPressSharedObjCUI static let styleAttr = try! NSRegularExpression(pattern: "\\s*style=\"[^\"]*\"", options: .caseInsensitive) // Gallery Images - static let galleryImgTags = try! NSRegularExpression(pattern: "]*data-orig-file[^>]*/>", options: .caseInsensitive) + public static let galleryImgTags = try! NSRegularExpression(pattern: "]*data-orig-file[^>]*/>", options: .caseInsensitive) // Trailing BR Tags static let trailingBRTags = try! NSRegularExpression(pattern: "(\\s*\\s*)+$", options: .caseInsensitive) @@ -38,31 +35,6 @@ import WordPressSharedObjCUI static let gutenbergGalleryListItem = try! NSRegularExpression(pattern: "]+gallery-item[^>]+>(
)", options: .caseInsensitive) } - /// Formats the specified content string for display. Forbidden HTML tags are - /// removed, paragraphs are normalized, etc. - /// - /// - Parameters: - /// - string: The content string to format. - /// - isPrivate: Whether the content is from a private blog. - /// - /// - Returns: The formatted string. - /// - @objc public class func formatContentString(_ string: String, isPrivateSite isPrivate: Bool) -> String { - guard !string.isEmpty else { - return string - } - - var content = string - content = removeForbiddenTags(content) - content = normalizeParagraphs(content) - content = removeInlineStyles(content) - content = (content as NSString).replacingHTMLEmoticonsWithEmoji() as String - content = formatGutenbergGallery(content) - content = resizeGalleryImageURL(content, isPrivateSite: isPrivate) - content = formatVideoTags(content) - return content - } - /// Removes forbidden HTML tags from the specified string. /// /// - Parameters: @@ -197,59 +169,6 @@ import WordPressSharedObjCUI return content } - /// Mutates gallery image URLs to be correctly sized. - /// - /// - Parameters: - /// - string: The content string to format. - /// - isPrivate: Whether the content is from a private blog. - /// - /// - Returns: The formatted string. - /// - @objc public class func resizeGalleryImageURL(_ string: String, isPrivateSite isPrivate: Bool) -> String { - guard !string.isEmpty else { - return string - } - - let imageSize = UIScreen.main.bounds.size - let scale = UIScreen.main.scale - let scaledSize = imageSize.applying(CGAffineTransform(scaleX: scale, y: scale)) - - let mContent = NSMutableString(string: string) - - let matches = RegEx.galleryImgTags.matches(in: mContent as String, options: [], range: NSRange(location: 0, length: mContent.length)) - - for match in matches.reversed() { - let imgElementStr = mContent.substring(with: match.range) - let srcImgURLStr = parseValueForAttribute("src", inElement: imgElementStr) - let originalImgURLStr = parseValueForAttribute("data-orig-file", inElement: imgElementStr) - - guard let originalURL = URL(string: originalImgURLStr) else { - continue - } - - var modifiedURL: URL - if isPrivate { - modifiedURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: originalURL) - } else { - modifiedURL = PhotonImageURLHelper.photonURL(with: imageSize, forImageURL: originalURL) - } - - guard modifiedURL.absoluteString.isEmpty() == false else { - continue - } - - let mImageStr = NSMutableString(string: imgElementStr) - mImageStr.replaceOccurrences(of: srcImgURLStr, - with: modifiedURL.absoluteString, - options: .literal, - range: NSRange(location: 0, length: imgElementStr.count)) - - mContent.replaceCharacters(in: match.range, with: mImageStr as String) - } - - return mContent as String - } - /// Parses the specified string for the value of the specified attribute. /// /// - Parameters: diff --git a/Modules/Sources/WordPressSharedUI/RichContentFormatter+DisplayPipeline.swift b/Modules/Sources/WordPressSharedUI/RichContentFormatter+DisplayPipeline.swift new file mode 100644 index 000000000000..84cf672f4f2a --- /dev/null +++ b/Modules/Sources/WordPressSharedUI/RichContentFormatter+DisplayPipeline.swift @@ -0,0 +1,97 @@ +import Foundation +import UIKit +import WordPressShared +import WordPressSharedObjCUI + +/// UIKit-dependent additions to `RichContentFormatter`. +/// +/// The platform-independent text transformations live in `WordPressShared`. The +/// display pipeline below depends on the screen (for gallery image sizing) and on +/// `WordPressSharedObjCUI`'s Photon helper, so it stays in the UI layer. +/// +extension RichContentFormatter { + + /// Formats the specified content string for display. Forbidden HTML tags are + /// removed, paragraphs are normalized, etc. + /// + /// - Parameters: + /// - string: The content string to format. + /// - isPrivate: Whether the content is from a private blog. + /// + /// - Returns: The formatted string. + /// + @objc public class func formatContentString(_ string: String, isPrivateSite isPrivate: Bool) -> String { + guard !string.isEmpty else { + return string + } + + var content = string + content = removeForbiddenTags(content) + content = normalizeParagraphs(content) + content = removeInlineStyles(content) + content = (content as NSString).replacingHTMLEmoticonsWithEmoji() as String + content = formatGutenbergGallery(content) + content = resizeGalleryImageURL(content, isPrivateSite: isPrivate) + content = formatVideoTags(content) + return content + } + + /// Mutates gallery image URLs to be correctly sized. + /// + /// - Parameters: + /// - string: The content string to format. + /// - isPrivate: Whether the content is from a private blog. + /// + /// - Returns: The formatted string. + /// + @objc public class func resizeGalleryImageURL(_ string: String, isPrivateSite isPrivate: Bool) -> String { + guard !string.isEmpty else { + return string + } + + let imageSize = UIScreen.main.bounds.size + let scale = UIScreen.main.scale + let scaledSize = imageSize.applying(CGAffineTransform(scaleX: scale, y: scale)) + + let mContent = NSMutableString(string: string) + + let matches = RegEx.galleryImgTags.matches( + in: mContent as String, + options: [], + range: NSRange(location: 0, length: mContent.length) + ) + + for match in matches.reversed() { + let imgElementStr = mContent.substring(with: match.range) + let srcImgURLStr = parseValueForAttribute("src", inElement: imgElementStr) + let originalImgURLStr = parseValueForAttribute("data-orig-file", inElement: imgElementStr) + + guard let originalURL = URL(string: originalImgURLStr) else { + continue + } + + var modifiedURL: URL + if isPrivate { + modifiedURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: originalURL) + } else { + modifiedURL = PhotonImageURLHelper.photonURL(with: imageSize, forImageURL: originalURL) + } + + guard modifiedURL.absoluteString.isEmpty() == false else { + continue + } + + let mImageStr = NSMutableString(string: imgElementStr) + mImageStr.replaceOccurrences( + of: srcImgURLStr, + with: modifiedURL.absoluteString, + options: .literal, + range: NSRange(location: 0, length: imgElementStr.count) + ) + + mContent.replaceCharacters(in: match.range, with: mImageStr as String) + } + + return mContent as String + } +} diff --git a/Modules/Tests/WordPressSharedTests/RichContentFormatterTests.swift b/Modules/Tests/WordPressSharedTests/RichContentFormatterTests.swift index e5068f17e019..b594c5482908 100644 --- a/Modules/Tests/WordPressSharedTests/RichContentFormatterTests.swift +++ b/Modules/Tests/WordPressSharedTests/RichContentFormatterTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import WordPressShared -@testable import WordPressSharedUI class RichContentFormatterTests: XCTestCase { @@ -32,10 +31,6 @@ class RichContentFormatterTests: XCTestCase { XCTAssertTrue(str == sanitizedStr, "Not all paragraphs were normalized.") } - func testResizeGalleryImageURLsForContentEmptyString() { - XCTAssertTrue("" == RichContentFormatter.resizeGalleryImageURL("", isPrivateSite: false)) - } - func testRemoveTrailingBRTags() { let str = "

test


test

" let styleStr = "

test


test



" diff --git a/Modules/Tests/WordPressSharedTests/RichContentFormatterUITests.swift b/Modules/Tests/WordPressSharedTests/RichContentFormatterUITests.swift new file mode 100644 index 000000000000..8efaddb06f04 --- /dev/null +++ b/Modules/Tests/WordPressSharedTests/RichContentFormatterUITests.swift @@ -0,0 +1,10 @@ +import XCTest +@testable import WordPressShared +@testable import WordPressSharedUI + +class RichContentFormatterUITests: XCTestCase { + + func testResizeGalleryImageURLsForContentEmptyString() { + XCTAssertTrue("" == RichContentFormatter.resizeGalleryImageURL("", isPrivateSite: false)) + } +} diff --git a/Package.swift b/Package.swift index 6a9397e7670e..c2eabdc904ba 100644 --- a/Package.swift +++ b/Package.swift @@ -43,7 +43,7 @@ let package = Package( path: "Modules/Tests/WordPressSharedTests", exclude: [ "WordPressShared.xctestplan", - "RichContentFormatterTests.swift", + "RichContentFormatterUITests.swift", "WPUserAgentTests.swift" ], swiftSettings: [.swiftLanguageMode(.v5)]