177 lines
6.3 KiB
Swift
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
|