metatext/Views/UIKit/Content Views/AccountView.swift

339 lines
14 KiB
Swift
Raw Normal View History

2020-09-23 01:00:56 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
import Kingfisher
2021-01-20 02:47:21 +00:00
import Mastodon
2020-09-23 01:00:56 +00:00
import UIKit
2021-01-26 06:57:44 +00:00
import ViewModels
2020-09-23 01:00:56 +00:00
2020-11-09 06:22:20 +00:00
final class AccountView: UIView {
2020-09-23 01:00:56 +00:00
let avatarImageView = AnimatedImageView()
2020-09-26 00:52:59 +00:00
let displayNameLabel = UILabel()
let accountLabel = UILabel()
2020-09-23 01:00:56 +00:00
let noteTextView = TouchFallthroughTextView()
2021-01-26 06:57:44 +00:00
let acceptFollowRequestButton = UIButton()
let rejectFollowRequestButton = UIButton()
2021-02-11 02:04:04 +00:00
let muteButton = UIButton(type: .system)
let unmuteButton = UIButton(type: .system)
let blockButton = UIButton(type: .system)
let unblockButton = UIButton(type: .system)
2020-09-23 01:00:56 +00:00
private var accountConfiguration: AccountContentConfiguration
init(configuration: AccountContentConfiguration) {
self.accountConfiguration = configuration
super.init(frame: .zero)
initialSetup()
2020-10-13 20:11:27 +00:00
applyAccountConfiguration()
2020-09-23 01:00:56 +00:00
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
2021-01-20 02:47:21 +00:00
extension AccountView {
2021-01-26 06:57:44 +00:00
static func estimatedHeight(width: CGFloat,
account: Account,
configuration: CollectionItem.AccountConfiguration) -> CGFloat {
var height = CGFloat.defaultSpacing * 2
+ .compactSpacing
2021-01-20 02:47:21 +00:00
+ account.displayName.height(width: width, font: .preferredFont(forTextStyle: .headline))
+ account.acct.height(width: width, font: .preferredFont(forTextStyle: .subheadline))
2021-01-26 06:57:44 +00:00
if configuration == .withNote {
height += .compactSpacing + account.note.attributed.string.height(
width: width,
font: .preferredFont(forTextStyle: .callout))
}
return max(height, .avatarDimension + .defaultSpacing * 2)
2021-01-20 02:47:21 +00:00
}
}
2020-09-23 01:00:56 +00:00
extension AccountView: UIContentView {
var configuration: UIContentConfiguration {
get { accountConfiguration }
set {
guard let accountConfiguration = newValue as? AccountContentConfiguration else { return }
self.accountConfiguration = accountConfiguration
applyAccountConfiguration()
}
}
}
2020-09-26 00:57:35 +00:00
extension AccountView: UITextViewDelegate {
func textView(
_ textView: UITextView,
shouldInteractWith URL: URL,
in characterRange: NSRange,
interaction: UITextItemInteraction) -> Bool {
switch interaction {
case .invokeDefaultAction:
accountConfiguration.viewModel.urlSelected(URL)
return false
case .preview: return false
case .presentActions: return false
@unknown default: return false
}
}
}
2020-09-23 01:00:56 +00:00
private extension AccountView {
2021-01-27 00:15:52 +00:00
// swiftlint:disable:next function_body_length
2020-09-23 01:00:56 +00:00
func initialSetup() {
2020-09-26 00:52:59 +00:00
let stackView = UIStackView()
2020-09-23 01:00:56 +00:00
2020-09-26 00:52:59 +00:00
addSubview(stackView)
2021-01-26 06:57:44 +00:00
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.spacing = .defaultSpacing
stackView.alignment = .top
stackView.addArrangedSubview(avatarImageView)
2020-10-12 05:37:34 +00:00
avatarImageView.layer.cornerRadius = .avatarDimension / 2
2020-09-26 00:52:59 +00:00
avatarImageView.clipsToBounds = true
2021-01-26 06:57:44 +00:00
let verticalStackView = UIStackView()
stackView.addArrangedSubview(verticalStackView)
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.axis = .vertical
verticalStackView.spacing = .compactSpacing
verticalStackView.addArrangedSubview(displayNameLabel)
verticalStackView.addArrangedSubview(accountLabel)
verticalStackView.addArrangedSubview(noteTextView)
2020-09-26 00:52:59 +00:00
displayNameLabel.numberOfLines = 0
displayNameLabel.font = .preferredFont(forTextStyle: .headline)
displayNameLabel.adjustsFontForContentSizeCategory = true
accountLabel.numberOfLines = 0
accountLabel.font = .preferredFont(forTextStyle: .subheadline)
accountLabel.adjustsFontForContentSizeCategory = true
accountLabel.textColor = .secondaryLabel
noteTextView.backgroundColor = .clear
2020-09-26 00:57:35 +00:00
noteTextView.delegate = self
2020-09-23 01:00:56 +00:00
2021-01-26 06:57:44 +00:00
let largeTitlePointSize = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
stackView.addArrangedSubview(acceptFollowRequestButton)
acceptFollowRequestButton.setImage(
UIImage(systemName: "checkmark.circle",
withConfiguration: UIImage.SymbolConfiguration(pointSize: largeTitlePointSize)),
for: .normal)
acceptFollowRequestButton.setContentHuggingPriority(.required, for: .horizontal)
acceptFollowRequestButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.acceptFollowRequest() },
for: .touchUpInside)
stackView.addArrangedSubview(rejectFollowRequestButton)
rejectFollowRequestButton.setImage(
UIImage(systemName: "xmark.circle",
withConfiguration: UIImage.SymbolConfiguration(pointSize: largeTitlePointSize)),
for: .normal)
rejectFollowRequestButton.tintColor = .systemRed
rejectFollowRequestButton.setContentHuggingPriority(.required, for: .horizontal)
rejectFollowRequestButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.rejectFollowRequest() },
for: .touchUpInside)
2021-02-11 02:04:04 +00:00
stackView.addArrangedSubview(muteButton)
muteButton.setTitle(NSLocalizedString("account.mute", comment: ""), for: .normal)
muteButton.titleLabel?.adjustsFontForContentSizeCategory = true
muteButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
muteButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmMute() },
for: .touchUpInside)
2021-02-11 02:39:24 +00:00
muteButton.isHidden = true
2021-02-11 02:04:04 +00:00
stackView.addArrangedSubview(unmuteButton)
unmuteButton.setTitle(NSLocalizedString("account.unmute", comment: ""), for: .normal)
unmuteButton.titleLabel?.adjustsFontForContentSizeCategory = true
unmuteButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
unmuteButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmUnmute() },
for: .touchUpInside)
2021-02-11 02:39:24 +00:00
unmuteButton.isHidden = true
2021-02-11 02:04:04 +00:00
stackView.addArrangedSubview(blockButton)
blockButton.setTitle(NSLocalizedString("account.block", comment: ""), for: .normal)
blockButton.titleLabel?.adjustsFontForContentSizeCategory = true
blockButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
blockButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmBlock() },
for: .touchUpInside)
2021-02-11 02:39:24 +00:00
blockButton.isHidden = true
2021-02-11 02:04:04 +00:00
stackView.addArrangedSubview(unblockButton)
unblockButton.setTitle(NSLocalizedString("account.unblock", comment: ""), for: .normal)
unblockButton.titleLabel?.adjustsFontForContentSizeCategory = true
unblockButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
unblockButton.addAction(
UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmUnblock() },
for: .touchUpInside)
2021-02-11 02:39:24 +00:00
unblockButton.isHidden = true
2021-02-11 02:04:04 +00:00
2020-09-23 01:00:56 +00:00
NSLayoutConstraint.activate([
2020-10-12 05:37:34 +00:00
avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
2021-01-26 06:57:44 +00:00
acceptFollowRequestButton.widthAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
acceptFollowRequestButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
rejectFollowRequestButton.widthAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
rejectFollowRequestButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
2021-02-11 02:04:04 +00:00
muteButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
unmuteButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
blockButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
unblockButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
2021-01-26 06:57:44 +00:00
stackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
2020-09-26 00:52:59 +00:00
stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
2021-01-26 06:57:44 +00:00
stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor)
2020-09-23 01:00:56 +00:00
])
2021-02-02 21:49:29 +00:00
isAccessibilityElement = true
2020-09-23 01:00:56 +00:00
}
2021-02-11 02:39:24 +00:00
// swiftlint:disable:next function_body_length cyclomatic_complexity
2020-09-23 01:00:56 +00:00
func applyAccountConfiguration() {
2021-01-26 06:57:44 +00:00
let viewModel = accountConfiguration.viewModel
2020-09-26 00:52:59 +00:00
2021-01-26 06:57:44 +00:00
avatarImageView.kf.setImage(with: viewModel.avatarURL(profile: false))
2021-02-02 21:49:29 +00:00
let mutableDisplayName = NSMutableAttributedString(string: viewModel.displayName)
2020-09-26 00:52:59 +00:00
2021-02-02 21:49:29 +00:00
mutableDisplayName.insert(emojis: viewModel.emojis, view: displayNameLabel)
mutableDisplayName.resizeAttachments(toLineHeight: displayNameLabel.font.lineHeight)
displayNameLabel.attributedText = mutableDisplayName
displayNameLabel.isHidden = viewModel.displayName.isEmpty
2020-09-26 00:52:59 +00:00
2021-01-26 06:57:44 +00:00
accountLabel.text = viewModel.accountName
if viewModel.configuration == .withNote {
let noteFont = UIFont.preferredFont(forTextStyle: .callout)
let mutableNote = NSMutableAttributedString(attributedString: viewModel.note)
let noteRange = NSRange(location: 0, length: mutableNote.length)
2020-09-26 00:52:59 +00:00
2021-01-26 06:57:44 +00:00
mutableNote.removeAttribute(.font, range: noteRange)
mutableNote.addAttributes(
[.font: noteFont as Any,
.foregroundColor: UIColor.label],
range: noteRange)
mutableNote.insert(emojis: viewModel.emojis, view: noteTextView)
mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)
noteTextView.attributedText = mutableNote
noteTextView.isHidden = false
} else {
noteTextView.isHidden = true
}
2020-09-26 00:52:59 +00:00
2021-01-26 06:57:44 +00:00
let isFollowRequest = viewModel.configuration == .followRequest
2020-09-26 00:52:59 +00:00
2021-01-26 06:57:44 +00:00
acceptFollowRequestButton.isHidden = !isFollowRequest
rejectFollowRequestButton.isHidden = !isFollowRequest
2021-02-02 21:49:29 +00:00
2021-02-11 02:04:04 +00:00
if let relationship = viewModel.relationship {
if viewModel.configuration == .mute {
muteButton.isHidden = relationship.muting
unmuteButton.isHidden = !relationship.muting
blockButton.isHidden = true
unblockButton.isHidden = true
} else if viewModel.configuration == .block {
muteButton.isHidden = true
unmuteButton.isHidden = true
blockButton.isHidden = relationship.blocking
unblockButton.isHidden = !relationship.blocking
}
} else {
muteButton.isHidden = true
unmuteButton.isHidden = true
blockButton.isHidden = true
unblockButton.isHidden = true
}
2021-02-02 21:49:29 +00:00
let accessibilityAttributedLabel = NSMutableAttributedString(string: "")
if !displayNameLabel.isHidden, let displayName = displayNameLabel.attributedText {
accessibilityAttributedLabel.append(displayName)
accessibilityAttributedLabel.appendWithSeparator(viewModel.accountName)
} else {
accessibilityAttributedLabel.appendWithSeparator(viewModel.accountName)
}
if !noteTextView.isHidden, let note = noteTextView.attributedText {
accessibilityAttributedLabel.appendWithSeparator(note)
}
self.accessibilityAttributedLabel = accessibilityAttributedLabel
if isFollowRequest {
accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.accept-follow-request-button.accessibility-label",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.acceptFollowRequest()
return true
},
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.reject-follow-request-button.accessibility-label",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.rejectFollowRequest()
return true
}]
2021-02-11 02:39:24 +00:00
} else if viewModel.configuration == .mute, let relationship = viewModel.relationship {
if relationship.muting {
accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.unmute",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.confirmUnmute()
return true
}]
} else {
accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.mute",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.confirmMute()
return true
}]
}
} else if viewModel.configuration == .block, let relationship = viewModel.relationship {
if relationship.blocking {
accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.unblock",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.confirmUnblock()
return true
}]
} else {
accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString(
"account.block",
comment: "")) { [weak self] _ in
self?.accountConfiguration.viewModel.confirmBlock()
return true
}]
}
2021-02-02 21:49:29 +00:00
} else {
accessibilityCustomActions = []
}
2020-09-23 01:00:56 +00:00
}
}