129 lines
4.5 KiB
Swift
129 lines
4.5 KiB
Swift
|
//
|
||
|
// ContentLabel.swift
|
||
|
// Tusker
|
||
|
//
|
||
|
// Created by Shadowfacts on 10/1/18.
|
||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import UIKit
|
||
|
import TTTAttributedLabel
|
||
|
import Pachyderm
|
||
|
import SwiftSoup
|
||
|
|
||
|
protocol ContentLabelNavigationDelegate {
|
||
|
func selected(mention: Mention)
|
||
|
|
||
|
func selected(tag: Hashtag)
|
||
|
|
||
|
func selected(url: URL)
|
||
|
}
|
||
|
|
||
|
class ContentLabel: TTTAttributedLabel {
|
||
|
|
||
|
var navigationDelegate: ContentLabelNavigationDelegate?
|
||
|
|
||
|
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))")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MARK: - Interaction
|
||
|
func linkTapped(_ label: TTTAttributedLabel!, _ link: TTTAttributedLabelLink!) {
|
||
|
if let navigationDelegate = navigationDelegate,
|
||
|
let url = link.result.url {
|
||
|
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!) {
|
||
|
// todo: long press options
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|