// // ActionNotificationTableViewCell.swift // Tusker // // Created by Shadowfacts on 9/2/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import Pachyderm class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive { var delegate: StatusTableViewCellDelegate? { didSet { contentLabel.navigationDelegate = delegate } } @IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var contentLabel: StatusContentLabel! @IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var opAvatarImageView: UIImageView! @IBOutlet weak var actionAvatarImageView: UIImageView! @IBOutlet weak var actionLabel: UILabel! @IBOutlet weak var timestampLabel: UILabel! @IBOutlet weak var attachmentsView: UIStackView! var notification: Pachyderm.Notification! var statusID: String! var opAvatarURL: URL? var actionAvatarURL: URL? var updateTimestampWorkItem: DispatchWorkItem? override func awakeFromNib() { displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) displayNameLabel.isUserInteractionEnabled = true usernameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) usernameLabel.isUserInteractionEnabled = true opAvatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) opAvatarImageView.isUserInteractionEnabled = true opAvatarImageView.layer.masksToBounds = true actionAvatarImageView.layer.masksToBounds = true actionLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(actionPressed))) actionLabel.isUserInteractionEnabled = true } func updateUIForPreferences() { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } opAvatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: opAvatarImageView) actionAvatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: actionAvatarImageView) displayNameLabel.text = status.account.realDisplayName let verb: String switch notification.kind { case .favourite: verb = "Liked" case .reblog: verb = "Reblogged" default: fatalError("Invalid notification type \(notification.kind) for ActionNotificationTableViewCell") } actionLabel.text = "\(verb) by \(notification.account.realDisplayName)" } func updateUI(for notification: Pachyderm.Notification) { guard notification.kind == .favourite || notification.kind == .reblog else { fatalError("Invalid notification type \(notification.kind) for ActionNotificationTableViewCell") } self.notification = notification let status = notification.status! self.statusID = status.id updateUIForPreferences() usernameLabel.text = "@\(status.account.acct)" opAvatarImageView.image = nil opAvatarURL = status.account.avatar ImageCache.avatars.get(status.account.avatar) { (data) in guard let data = data else { return } DispatchQueue.main.async { self.opAvatarImageView.image = UIImage(data: data) self.opAvatarURL = nil } } actionAvatarImageView.image = nil actionAvatarURL = notification.account.avatar ImageCache.avatars.get(notification.account.avatar) { (data) in guard let data = data else { return } DispatchQueue.main.async { self.actionAvatarImageView.image = UIImage(data: data) self.actionAvatarURL = nil } } updateTimestamp() let attachments = status.attachments.filter({ $0.kind == .image }) if attachments.count > 0 { attachmentsView.isHidden = false for attachment in attachments { let url = attachment.textURL ?? attachment.url let label = UILabel() label.textColor = .darkGray let textAttachment = InlineTextAttachment() textAttachment.image = UIImage(named: "Link")! textAttachment.bounds = CGRect(x: 0, y: 0, width: label.font.pointSize, height: label.font.pointSize) textAttachment.fontDescender = label.font.descender let attachmentStr = NSAttributedString(attachment: textAttachment) let text = NSMutableAttributedString(string: " ") text.append(attachmentStr) text.append(NSAttributedString(string: " ")) // text.addAttribute(.font, value: UIFont.systemFont(ofSize: 0), range: NSRange(location: 0, length: text.length)) text.append(NSAttributedString(string: "\(url.lastPathComponent)")) text.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: 2)) // let text = NSMutableAttributedString(string: " \(url.lastPathComponent)") // let imageAttachment = InlineTextAttachment() // imageAttachment.image = UIImage(named: "Link")! // imageAttachment.bounds = CGRect(x: 0, y: 0, width: label.font.pointSize, height: label.font.pointSize) // imageAttachment.fontDescender = label.font.descender // let imageStr = NSMutableAttributedString(attachment: imageAttachment) // imageStr.setAttributes([.foregroundColor: UIColor.darkGray], range: ) // text.insert(imageStr, at: 0) label.attributedText = text attachmentsView.addArrangedSubview(label) } } else { attachmentsView.isHidden = true } // contentLabel.statusID = status.id contentLabel.setTextFromHtml(status.content) } func updateTimestamp() { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } timestampLabel.text = status.createdAt.timeAgoString() let delay: DispatchTimeInterval? switch status.createdAt.timeAgo().1 { case .second: delay = .seconds(10) case .minute: delay = .seconds(60) default: delay = nil } if let delay = delay { updateTimestampWorkItem = DispatchWorkItem { self.updateTimestamp() } DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!) } else { updateTimestampWorkItem = nil } } override func prepareForReuse() { if let url = opAvatarURL { ImageCache.avatars.cancel(url) } if let url = actionAvatarURL { ImageCache.avatars.cancel(url) } updateTimestampWorkItem?.cancel() updateTimestampWorkItem = nil attachmentsView.subviews.forEach { $0.removeFromSuperview() } } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) if selected { delegate?.selected(status: statusID) } } @objc func accountPressed() { delegate?.selected(account: notification.status!.account.id) } @objc func actionPressed() { delegate?.selected(account: notification.account.id) } } extension ActionNotificationTableViewCell: PreviewViewControllerProvider { func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? { if avatarContainerView.frame.contains(location) { return delegate?.router.profile(for: notification.account.id) } else if contentLabel.frame.contains(location), let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) { return vc } return delegate?.router.conversation(for: statusID) } }