Tusker/Tusker/Screens/Explore/TrendingLinkCardCollectionV...

177 lines
6.3 KiB
Swift

//
// TrendingLinkCardCollectionViewCell.swift
// Tusker
//
// Created by Shadowfacts on 6/29/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
#if !os(visionOS)
import UIKit
import Pachyderm
import WebURLFoundationExtras
import HTMLStreamer
class TrendingLinkCardCollectionViewCell: UICollectionViewCell {
private var card: Card?
@IBOutlet weak var thumbnailView: CachedImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var providerLabel: UILabel!
@IBOutlet weak var activityLabel: UILabel!
@IBOutlet weak var historyView: TrendHistoryView!
private var thumbnailAspectRatioConstraint: NSLayoutConstraint?
var verticalSize: VerticalSize! {
didSet {
guard oldValue != verticalSize else {
return
}
switch verticalSize {
case nil:
fatalError()
case .regular:
thumbnailAspectRatioConstraint?.isActive = false
thumbnailAspectRatioConstraint = thumbnailView.widthAnchor.constraint(equalTo: thumbnailView.heightAnchor, multiplier: 4/3)
thumbnailAspectRatioConstraint!.isActive = true
descriptionLabel.numberOfLines = 3
case .compact:
thumbnailAspectRatioConstraint?.isActive = false
thumbnailAspectRatioConstraint = thumbnailView.widthAnchor.constraint(equalTo: thumbnailView.heightAnchor, multiplier: 2/1)
thumbnailAspectRatioConstraint!.isActive = true
descriptionLabel.numberOfLines = 5
}
}
}
private var hoverGestureAnimator: UIViewPropertyAnimator?
override func awakeFromNib() {
super.awakeFromNib()
thumbnailView.cache = .attachments
layer.shadowOpacity = 0.2
layer.shadowRadius = 8
layer.shadowOffset = .zero
layer.masksToBounds = false
contentView.layer.cornerRadius = 12.5
contentView.layer.cornerCurve = .continuous
contentView.backgroundColor = .appGroupedCellBackground
updateLayerColors()
verticalSize = .regular
addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(hoverRecognized)))
}
func updateUI(card: Card) {
self.card = card
self.thumbnailView.image = nil
thumbnailView.update(for: card.image.flatMap { URL($0) }, blurhash: card.blurhash)
let title = card.title.trimmingCharacters(in: .whitespacesAndNewlines)
titleLabel.text = title
let provider = card.providerName!.trimmingCharacters(in: .whitespacesAndNewlines)
providerLabel.text = provider
let converter = TextConverter(configuration: .init(insertNewlines: false), callbacks: HTMLConverter.Callbacks.self)
descriptionLabel.text = converter.convert(html: card.description)
descriptionLabel.isHidden = description.isEmpty
let sorted = card.history!.sorted(by: { $0.day < $1.day })
let lastTwo = sorted[(sorted.count - 2)...]
let accounts = lastTwo.map(\.accounts).reduce(0, +)
let uses = lastTwo.map(\.uses).reduce(0, +)
// U+2009 THIN SPACE
let activityStr = NSMutableAttributedString(string: "\(accounts.formatted())\u{2009}")
activityStr.append(NSAttributedString(attachment: NSTextAttachment(image: UIImage(systemName: "person")!)))
activityStr.append(NSAttributedString(string: ", \(uses.formatted())\u{2009}"))
activityStr.append(NSAttributedString(attachment: NSTextAttachment(image: UIImage(systemName: "square.text.square")!)))
activityLabel.attributedText = activityStr
historyView.setHistory(card.history)
historyView.isHidden = card.history == nil || card.history!.count < 2
}
private func loadBlurHash(card: Card) {
guard let hash = card.blurhash else {
return
}
AttachmentView.queue.async { [weak self] in
let size: CGSize
if let width = card.width, let height = card.height {
let aspectRatio = CGFloat(width) / CGFloat(height)
if aspectRatio > 1 {
size = CGSize(width: 32, height: 32 / aspectRatio)
} else {
size = CGSize(width: 32 * aspectRatio, height: 32)
}
} else {
size = CGSize(width: 32, height: 32)
}
guard let preview = UIImage(blurHash: hash, size: size) else {
return
}
DispatchQueue.main.async { [weak self] in
guard let self,
self.card?.url == card.url,
self.thumbnailView.image == nil else {
return
}
self.thumbnailView.image = preview
}
}
}
// Unneeded on visionOS because there is no light/dark mode
#if !os(visionOS)
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateLayerColors()
}
#endif
private func updateLayerColors() {
if traitCollection.userInterfaceStyle == .dark {
layer.shadowColor = UIColor.darkGray.cgColor
} else {
layer.shadowColor = UIColor.black.cgColor
}
}
// MARK: Interaction
@objc private func hoverRecognized(_ recognizer: UIHoverGestureRecognizer) {
switch recognizer.state {
case .began, .changed:
hoverGestureAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: {
self.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
})
hoverGestureAnimator!.startAnimation()
case .ended:
hoverGestureAnimator?.stopAnimation(true)
hoverGestureAnimator?.addAnimations {
self.transform = .identity
}
hoverGestureAnimator?.startAnimation()
default:
break
}
}
}
extension TrendingLinkCardCollectionViewCell {
enum VerticalSize {
case regular, compact
}
}
#endif