A WIP iOS app for Mastodon and Pleroma.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.1 KiB

//
// 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
}
}
}