// // TrendingLinkCardCollectionViewCell.swift // Tusker // // Created by Shadowfacts on 6/29/22. // Copyright © 2022 Shadowfacts. All rights reserved. // import UIKit import Pachyderm import WebURLFoundationExtras import SwiftSoup 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 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 updateLayerColors() 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 description = try! SwiftSoup.parseBodyFragment(card.description).text() descriptionLabel.text = 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 } } } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) updateLayerColors() } 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 } } }