metatext/Views/UIKit/Content Views/ConversationView.swift

161 lines
6.5 KiB
Swift
Raw Normal View History

2020-10-29 06:03:45 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
2021-01-20 02:47:21 +00:00
import Mastodon
2020-10-29 06:03:45 +00:00
import UIKit
2021-01-20 02:47:21 +00:00
import ViewModels
2020-10-29 06:03:45 +00:00
final class ConversationView: UIView {
let avatarsView = ConversationAvatarsView()
2021-02-22 07:10:34 +00:00
let displayNamesLabel = AnimatedAttachmentLabel()
2021-02-20 01:42:45 +00:00
let unreadIndicator = UIImageView(image: UIImage(
systemName: "circlebadge.fill",
withConfiguration: UIImage.SymbolConfiguration(scale: .small)))
2021-01-27 02:42:22 +00:00
let timeLabel = UILabel()
2020-10-29 06:03:45 +00:00
let statusBodyView = StatusBodyView()
private var conversationConfiguration: ConversationContentConfiguration
init(configuration: ConversationContentConfiguration) {
conversationConfiguration = configuration
super.init(frame: .zero)
initialSetup()
applyConversationConfiguration()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
2021-01-20 02:47:21 +00:00
extension ConversationView {
static func estimatedHeight(width: CGFloat,
2021-01-26 00:06:35 +00:00
identityContext: IdentityContext,
2021-01-20 02:47:21 +00:00
conversation: Conversation) -> CGFloat {
guard let status = conversation.lastStatus else { return UITableView.automaticDimension }
let bodyWidth = width - .defaultSpacing - .avatarDimension
return .defaultSpacing * 2
+ UIFont.preferredFont(forTextStyle: .headline).lineHeight
+ StatusBodyView.estimatedHeight(
width: bodyWidth,
2021-01-26 00:06:35 +00:00
identityContext: identityContext,
2021-01-20 02:47:21 +00:00
status: status,
configuration: .default)
}
}
2020-10-29 06:03:45 +00:00
extension ConversationView: UIContentView {
var configuration: UIContentConfiguration {
get { conversationConfiguration }
set {
guard let conversationConfiguration = newValue as? ConversationContentConfiguration else { return }
self.conversationConfiguration = conversationConfiguration
applyConversationConfiguration()
}
}
}
private extension ConversationView {
2021-02-02 21:32:39 +00:00
// swiftlint:disable:next function_body_length
2020-10-29 06:03:45 +00:00
func initialSetup() {
let containerStackView = UIStackView()
let sideStackView = UIStackView()
let mainStackView = UIStackView()
2021-01-27 02:42:22 +00:00
let namesTimeStackView = UIStackView()
2020-10-29 06:03:45 +00:00
addSubview(containerStackView)
containerStackView.translatesAutoresizingMaskIntoConstraints = false
containerStackView.spacing = .defaultSpacing
2021-01-27 02:42:22 +00:00
sideStackView.alignment = .top
2020-10-29 06:03:45 +00:00
sideStackView.spacing = .compactSpacing
sideStackView.addArrangedSubview(avatarsView)
containerStackView.addArrangedSubview(sideStackView)
2021-01-27 02:42:22 +00:00
namesTimeStackView.spacing = .compactSpacing
namesTimeStackView.alignment = .top
namesTimeStackView.addArrangedSubview(displayNamesLabel)
2021-02-20 01:42:45 +00:00
namesTimeStackView.addArrangedSubview(unreadIndicator)
2021-01-27 02:42:22 +00:00
namesTimeStackView.addArrangedSubview(timeLabel)
2020-10-29 06:03:45 +00:00
mainStackView.axis = .vertical
mainStackView.spacing = .compactSpacing
2021-01-27 02:42:22 +00:00
mainStackView.addArrangedSubview(namesTimeStackView)
2020-10-29 06:03:45 +00:00
mainStackView.addArrangedSubview(statusBodyView)
containerStackView.addArrangedSubview(mainStackView)
displayNamesLabel.font = .preferredFont(forTextStyle: .headline)
displayNamesLabel.adjustsFontForContentSizeCategory = true
2021-01-27 02:42:22 +00:00
displayNamesLabel.numberOfLines = 0
2021-02-20 01:42:45 +00:00
unreadIndicator.contentMode = .scaleAspectFit
unreadIndicator.setContentHuggingPriority(.required, for: .horizontal)
2021-01-27 02:42:22 +00:00
timeLabel.font = .preferredFont(forTextStyle: .subheadline)
timeLabel.adjustsFontForContentSizeCategory = true
timeLabel.textColor = .secondaryLabel
timeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
timeLabel.setContentHuggingPriority(.required, for: .horizontal)
2020-10-29 06:03:45 +00:00
statusBodyView.alpha = 0.5
statusBodyView.isUserInteractionEnabled = false
let avatarsHeightConstraint = avatarsView.heightAnchor.constraint(equalToConstant: .avatarDimension)
avatarsHeightConstraint.priority = .justBelowMax
NSLayoutConstraint.activate([
containerStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
containerStackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
containerStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
containerStackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
avatarsView.widthAnchor.constraint(equalToConstant: .avatarDimension),
avatarsHeightConstraint,
sideStackView.widthAnchor.constraint(equalToConstant: .avatarDimension)
])
2021-02-02 21:32:39 +00:00
isAccessibilityElement = true
2020-10-29 06:03:45 +00:00
}
func applyConversationConfiguration() {
let viewModel = conversationConfiguration.viewModel
let displayNames = ListFormatter.localizedString(byJoining: viewModel.accountViewModels.map(\.displayName))
let mutableDisplayNames = NSMutableAttributedString(string: displayNames)
mutableDisplayNames.insert(
2021-01-12 07:33:35 +00:00
emojis: viewModel.accountViewModels.map(\.emojis).reduce([], +),
2021-02-22 07:10:34 +00:00
view: displayNamesLabel,
identityContext: viewModel.identityContext)
2020-10-29 06:03:45 +00:00
mutableDisplayNames.resizeAttachments(toLineHeight: displayNamesLabel.font.lineHeight)
2021-02-20 01:42:45 +00:00
unreadIndicator.isHidden = !viewModel.isUnread
2020-10-29 06:03:45 +00:00
displayNamesLabel.attributedText = mutableDisplayNames
2021-01-27 02:42:22 +00:00
timeLabel.text = viewModel.statusViewModel?.time
2021-02-02 21:32:39 +00:00
timeLabel.accessibilityLabel = viewModel.statusViewModel?.accessibilityTime
2020-10-29 06:03:45 +00:00
statusBodyView.viewModel = viewModel.statusViewModel
avatarsView.viewModel = viewModel
2021-02-02 21:32:39 +00:00
let accessibilityAttributedLabel = NSMutableAttributedString(attributedString: mutableDisplayNames)
2021-02-20 01:42:45 +00:00
if viewModel.isUnread {
accessibilityAttributedLabel.appendWithSeparator(NSLocalizedString("conversation.unread", comment: ""))
}
2021-02-02 21:32:39 +00:00
if let statusBodyAccessibilityAttributedLabel = statusBodyView.accessibilityAttributedLabel {
accessibilityAttributedLabel.appendWithSeparator(statusBodyAccessibilityAttributedLabel)
}
if let accessibilityTime = viewModel.statusViewModel?.accessibilityTime {
accessibilityAttributedLabel.appendWithSeparator(accessibilityTime)
}
self.accessibilityAttributedLabel = accessibilityAttributedLabel
2020-10-29 06:03:45 +00:00
}
}