// // ContentLabel.swift // Tusker // // Created by Shadowfacts on 10/1/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import TTTAttributedLabel import Pachyderm import SwiftSoup class ContentLabel: TTTAttributedLabel { var navigationDelegate: TuskerNavigationDelegate? override func awakeFromNib() { super.awakeFromNib() isUserInteractionEnabled = true } // MARK: - HTML Parsing func setTextFromHtml(_ html: String) { let doc = try! SwiftSoup.parse(html) let body = doc.body()! let (attributedText, links) = attributedTextForHTMLNode(body) let mutAttrString = NSMutableAttributedString(attributedString: attributedText) mutAttrString.trimCharactersInSet(.whitespacesAndNewlines) mutAttrString.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutAttrString.length)) self.text = mutAttrString for (range, url) in links { let result = NSTextCheckingResult.linkCheckingResult(range: range, url: url) let link = TTTAttributedLabelLink(attributes: [ NSAttributedString.Key.foregroundColor: UIColor.blue ], activeAttributes: [ kTTTBackgroundFillColorAttributeName: UIColor(displayP3Red: 0.9, green: 0.9, blue: 0.9, alpha: 1), kTTTBackgroundFillPaddingAttributeName: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2), kTTTBackgroundCornerRadiusAttributeName: NSNumber(value: 5) ], inactiveAttributes: [:], textCheckingResult: result)! link.linkTapBlock = self.linkTapped link.linkLongPressBlock = self.linkLongPressed addLink(link) } } private func attributedTextForHTMLNode(_ node: Node) -> (NSAttributedString, [NSRange: URL]) { switch node { case let node as TextNode: return (NSAttributedString(string: node.text()), [:]) case let node as Element: var links = [NSRange: URL]() let attributed = NSMutableAttributedString() for child in node.getChildNodes() { let (text, childLinks) = attributedTextForHTMLNode(child) for (range, url) in childLinks { let newRange = NSRange(location: range.location + attributed.length, length: range.length) links[newRange] = url } attributed.append(text) } switch node.tagName() { case "br": attributed.append(NSAttributedString(string: "\n")) case "a": if let link = try? node.attr("href"), let url = URL(string: link) { let linkRange = NSRange(location: 0, length: attributed.length) links[linkRange] = url } case "p": attributed.append(NSAttributedString(string: "\n\n")) default: break } return (attributed, links) default: fatalError("Unexpected node type: \(type(of: node))") } } func getViewController(forLinkAt point: CGPoint) -> UIViewController? { guard let navigationDelegate = navigationDelegate, let link = link(at: point), let url = link.result.url else { return nil } let text = (self.text as! NSString).substring(with: link.result.range) if let mention = getMention(for: url, text: text) { return navigationDelegate.router.profile(for: mention) } else if let tag = getHashtag(for: url, text: text) { return navigationDelegate.router.timeline(tag: tag) } else { return navigationDelegate.router.safariViewController(for: url) } } // MARK: - Interaction func linkTapped(_ label: TTTAttributedLabel!, _ link: TTTAttributedLabelLink!) { guard let navigationDelegate = navigationDelegate, let url = link.result.url else { return } let text = (self.text as! NSString).substring(with: link.result.range) if let mention = getMention(for: url, text: text) { navigationDelegate.selected(mention: mention) } else if let tag = getHashtag(for: url, text: text) { navigationDelegate.selected(tag: tag) } else { navigationDelegate.selected(url: url) } } func linkLongPressed(_ label: TTTAttributedLabel!, _ link: TTTAttributedLabelLink!) { guard let url = link.result.url else { return } navigationDelegate?.showMoreOptions(forURL: url) } // MARK: - Navigation func getMention(for url: URL, text: String) -> Mention? { return nil } func getHashtag(for url: URL, text: String) -> Hashtag? { if text.starts(with: "#") { let tag = String(text.dropFirst()) return Hashtag(name: tag, url: url) } else { return nil } } }