Tusker/Tusker/Views/Notifications/FollowRequestNotificationTa...

221 lines
7.9 KiB
Swift

//
// FollowRequestNotificationTableViewCell.swift
// Tusker
//
// Created by Shadowfacts on 1/4/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
class FollowRequestNotificationTableViewCell: UITableViewCell {
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var stackView: UIStackView!
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var timestampLabel: UILabel!
@IBOutlet weak var actionLabel: EmojiLabel!
@IBOutlet weak var actionButtonsStackView: UIStackView!
@IBOutlet weak var acceptButton: UIButton!
@IBOutlet weak var rejectButton: UIButton!
var notification: Pachyderm.Notification?
var account: Account!
private var avatarRequest: ImageCache.Request?
private var updateTimestampWorkItem: DispatchWorkItem?
private var isGrayscale = false
deinit {
updateTimestampWorkItem?.cancel()
}
override func awakeFromNib() {
super.awakeFromNib()
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
avatarImageView.layer.masksToBounds = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
updateUIForPreferences()
}
@objc func updateUIForPreferences() {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
if isGrayscale != Preferences.shared.grayscaleImages,
let account = self.account {
updateUI(account: account)
}
}
func updateUI(notification: Pachyderm.Notification) {
self.notification = notification
updateUI(account: notification.account)
updateTimestamp()
}
func updateUI(account: Account) {
// todo: update to use managed objects
self.account = account
if Preferences.shared.hideCustomEmojiInUsernames {
actionLabel.text = "Request to follow from \(account.displayName)"
actionLabel.removeEmojis()
} else {
actionLabel.text = "Request to follow from \(account.displayName)"
actionLabel.setEmojis(account.emojis, identifier: account.id)
}
if let avatarURL = account.avatar {
avatarRequest = ImageCache.avatars.get(avatarURL) { [weak self] (_, image) in
guard let self = self else { return }
self.avatarRequest = nil
guard self.account == account,
let image = image,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) else {
return
}
DispatchQueue.main.async {
self.avatarImageView.image = transformedImage
}
}
}
}
private func updateTimestamp() {
guard let notification = notification else { return }
timestampLabel.text = notification.createdAt.timeAgoString()
let delay: DispatchTimeInterval?
switch notification.createdAt.timeAgo().1 {
case .second:
delay = .seconds(10)
case .minute:
delay = .seconds(60)
default:
delay = nil
}
if let delay = delay {
if updateTimestampWorkItem == nil {
updateTimestampWorkItem = DispatchWorkItem { [weak self] in
self?.updateTimestamp()
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
} else {
updateTimestampWorkItem = nil
}
}
override func prepareForReuse() {
super.prepareForReuse()
avatarRequest?.cancel()
updateTimestampWorkItem?.cancel()
updateTimestampWorkItem = nil
}
private func addLabel(_ text: String) {
let label = UILabel()
label.textAlignment = .center
label.font = .boldSystemFont(ofSize: 17)
label.text = text
self.stackView.addArrangedSubview(label)
}
// MARK: - Interaction
@IBAction func rejectButtonPressed() {
acceptButton.isEnabled = false
rejectButton.isEnabled = false
Task {
let request = Account.rejectFollowRequest(account)
do {
_ = try await mastodonController.run(request)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
self.actionButtonsStackView.isHidden = true
self.addLabel(NSLocalizedString("Rejected", comment: "rejected follow request label"))
} catch let error as Client.Error {
acceptButton.isEnabled = true
rejectButton.isEnabled = true
if let toastable = delegate?.toastableViewController {
let config = ToastConfiguration(from: error, with: "Rejecting Follow", in: toastable) { [weak self] toast in
toast.dismissToast(animated: true)
self?.rejectButtonPressed()
}
toastable.showToast(configuration: config, animated: true)
}
}
}
}
@IBAction func acceptButtonPressed() {
acceptButton.isEnabled = false
rejectButton.isEnabled = false
Task {
let request = Account.authorizeFollowRequest(account)
do {
_ = try await mastodonController.run(request)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
self.actionButtonsStackView.isHidden = true
self.addLabel(NSLocalizedString("Accepted", comment: "accepted follow request label"))
} catch let error as Client.Error {
acceptButton.isEnabled = true
rejectButton.isEnabled = true
if let toastable = delegate?.toastableViewController {
let config = ToastConfiguration(from: error, with: "Accepting Follow", in: toastable) { [weak self] toast in
toast.dismissToast(animated: true)
self?.acceptButtonPressed()
}
toastable.showToast(configuration: config, animated: true)
}
}
}
}
}
extension FollowRequestNotificationTableViewCell: SelectableTableViewCell {
func didSelectCell() {
delegate?.selected(account: account.id)
}
}
extension FollowRequestNotificationTableViewCell: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (content: {
return ProfileViewController(accountID: self.account.id, mastodonController: mastodonController)
}, actions: {
return []
})
}
}
extension FollowRequestNotificationTableViewCell: DraggableTableViewCell {
func dragItemsForBeginning(session: UIDragSession) -> [UIDragItem] {
let provider = NSItemProvider(object: account.url as NSURL)
let activity = UserActivityManager.showProfileActivity(id: account.id, accountID: mastodonController.accountInfo!.id)
activity.displaysAuxiliaryScene = true
provider.registerObject(activity, visibility: .all)
return [UIDragItem(itemProvider: provider)]
}
}