// // TimelineStatusTableViewCell.swift // Tusker // // Created by Shadowfacts on 8/16/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import Combine import Pachyderm class TimelineStatusTableViewCell: BaseStatusTableViewCell { static let relativeDateFormatter: RelativeDateTimeFormatter = { let formatter = RelativeDateTimeFormatter() formatter.dateTimeStyle = .numeric formatter.unitsStyle = .short return formatter }() @IBOutlet weak var reblogLabel: UILabel! @IBOutlet weak var timestampLabel: UILabel! @IBOutlet weak var pinImageView: UIImageView! var reblogStatusID: String? var rebloggerID: String? var showPinned: Bool = false var updateTimestampWorkItem: DispatchWorkItem? var rebloggerAccountUpdater: Cancellable? deinit { rebloggerAccountUpdater?.cancel() } override func awakeFromNib() { super.awakeFromNib() reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed))) accessibilityElements!.insert(reblogLabel!, at: 0) rebloggerAccountUpdater = MastodonCache.accountSubject .filter { $0.id == self.rebloggerID } .receive(on: DispatchQueue.main) .sink(receiveValue: updateRebloggerLabel(reblogger:)) } override func updateUI(statusID: String) { guard var status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } let realStatusID: String if let rebloggedStatusID = status.reblog?.id, let rebloggedStatus = MastodonCache.status(for: rebloggedStatusID) { reblogStatusID = statusID rebloggerID = status.account.id status = rebloggedStatus realStatusID = rebloggedStatus.id reblogLabel.isHidden = false } else { reblogStatusID = nil rebloggerID = nil reblogLabel.isHidden = true realStatusID = statusID } super.updateUI(statusID: realStatusID) updateTimestamp() let pinned = status.pinned ?? false pinImageView.isHidden = !(pinned && showPinned) timestampLabel.isHidden = !pinImageView.isHidden } @objc override func updateUIForPreferences() { super.updateUIForPreferences() if let rebloggerID = rebloggerID, let reblogger = MastodonCache.account(for: rebloggerID) { updateRebloggerLabel(reblogger: reblogger) } } private func updateRebloggerLabel(reblogger: Account) { reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)" } func updateTimestamp() { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } timestampLabel.text = status.createdAt.timeAgoString() timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date()) 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() { super.prepareForReuse() updateTimestampWorkItem?.cancel() updateTimestampWorkItem = nil showPinned = false } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) if selected { delegate?.selected(status: statusID) } } @objc func reblogLabelPressed() { guard let rebloggerID = rebloggerID else { return } delegate?.selected(account: rebloggerID) } override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? { return ( content: { ConversationTableViewController(for: self.statusID) }, actions: { self.actionsForStatus(statusID: self.statusID) } ) } } extension TimelineStatusTableViewCell: TableViewSwipeActionProvider { func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } let favoriteTitle: String let favoriteRequest: Request let favoriteColor: UIColor if status.favourited ?? false { favoriteTitle = "Unfavorite" favoriteRequest = Status.unfavourite(status) favoriteColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) } else { favoriteTitle = "Favorite" favoriteRequest = Status.favourite(status) favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1) } let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in MastodonController.client.run(favoriteRequest, completion: { response in DispatchQueue.main.async { guard case let .success(status, _) = response else { completion(false) return } completion(true) MastodonCache.add(status: status) } }) } favorite.image = UIImage(systemName: "star.fill") favorite.backgroundColor = favoriteColor let reblogTitle: String let reblogRequest: Request let reblogColor: UIColor if status.reblogged ?? false { reblogTitle = "Unreblog" reblogRequest = Status.unreblog(status) reblogColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) } else { reblogTitle = "Reblog" reblogRequest = Status.reblog(status) reblogColor = tintColor } let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in MastodonController.client.run(reblogRequest, completion: { response in DispatchQueue.main.async { guard case let .success(status, _) = response else { completion(false) return } completion(true) MastodonCache.add(status: status) } }) } reblog.image = UIImage(systemName: "repeat") reblog.backgroundColor = reblogColor return UISwipeActionsConfiguration(actions: [favorite, reblog]) } func trailingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? { let reply = UIContextualAction(style: .normal, title: "Reply") { (action, view, completion) in completion(true) self.delegate?.reply(to: self.statusID) } reply.image = UIImage(systemName: "arrowshape.turn.up.left.fill") reply.backgroundColor = tintColor let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in completion(true) self.delegate?.showMoreOptions(forStatus: self.statusID) } more.image = UIImage(systemName: "ellipsis") more.backgroundColor = .gray return UISwipeActionsConfiguration(actions: [reply, more]) } }