2020-10-29 07:03:45 +01:00
|
|
|
// Copyright © 2020 Metabolist. All rights reserved.
|
|
|
|
|
2021-01-20 03:47:21 +01:00
|
|
|
import Mastodon
|
2020-10-29 07:03:45 +01:00
|
|
|
import UIKit
|
2021-01-20 03:47:21 +01:00
|
|
|
import ViewModels
|
2020-10-29 07:03:45 +01:00
|
|
|
|
|
|
|
final class ConversationView: UIView {
|
|
|
|
let avatarsView = ConversationAvatarsView()
|
|
|
|
let displayNamesLabel = UILabel()
|
2021-01-27 03:42:22 +01:00
|
|
|
let timeLabel = UILabel()
|
2020-10-29 07:03:45 +01: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 03:47:21 +01:00
|
|
|
extension ConversationView {
|
|
|
|
static func estimatedHeight(width: CGFloat,
|
2021-01-26 01:06:35 +01:00
|
|
|
identityContext: IdentityContext,
|
2021-01-20 03:47:21 +01: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 01:06:35 +01:00
|
|
|
identityContext: identityContext,
|
2021-01-20 03:47:21 +01:00
|
|
|
status: status,
|
|
|
|
configuration: .default)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-29 07:03:45 +01: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 {
|
|
|
|
func initialSetup() {
|
|
|
|
let containerStackView = UIStackView()
|
|
|
|
let sideStackView = UIStackView()
|
|
|
|
let mainStackView = UIStackView()
|
2021-01-27 03:42:22 +01:00
|
|
|
let namesTimeStackView = UIStackView()
|
2020-10-29 07:03:45 +01:00
|
|
|
|
|
|
|
addSubview(containerStackView)
|
|
|
|
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
containerStackView.spacing = .defaultSpacing
|
|
|
|
|
2021-01-27 03:42:22 +01:00
|
|
|
sideStackView.alignment = .top
|
2020-10-29 07:03:45 +01:00
|
|
|
sideStackView.spacing = .compactSpacing
|
|
|
|
sideStackView.addArrangedSubview(avatarsView)
|
|
|
|
containerStackView.addArrangedSubview(sideStackView)
|
|
|
|
|
2021-01-27 03:42:22 +01:00
|
|
|
namesTimeStackView.spacing = .compactSpacing
|
|
|
|
namesTimeStackView.alignment = .top
|
|
|
|
namesTimeStackView.addArrangedSubview(displayNamesLabel)
|
|
|
|
namesTimeStackView.addArrangedSubview(timeLabel)
|
|
|
|
|
2020-10-29 07:03:45 +01:00
|
|
|
mainStackView.axis = .vertical
|
|
|
|
mainStackView.spacing = .compactSpacing
|
2021-01-27 03:42:22 +01:00
|
|
|
mainStackView.addArrangedSubview(namesTimeStackView)
|
2020-10-29 07:03:45 +01:00
|
|
|
mainStackView.addArrangedSubview(statusBodyView)
|
|
|
|
containerStackView.addArrangedSubview(mainStackView)
|
|
|
|
|
|
|
|
displayNamesLabel.font = .preferredFont(forTextStyle: .headline)
|
|
|
|
displayNamesLabel.adjustsFontForContentSizeCategory = true
|
2021-01-27 03:42:22 +01:00
|
|
|
displayNamesLabel.numberOfLines = 0
|
|
|
|
|
|
|
|
timeLabel.font = .preferredFont(forTextStyle: .subheadline)
|
|
|
|
timeLabel.adjustsFontForContentSizeCategory = true
|
|
|
|
timeLabel.textColor = .secondaryLabel
|
|
|
|
timeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
timeLabel.setContentHuggingPriority(.required, for: .horizontal)
|
2020-10-29 07:03:45 +01: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)
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
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 08:33:35 +01:00
|
|
|
emojis: viewModel.accountViewModels.map(\.emojis).reduce([], +),
|
2020-10-29 07:03:45 +01:00
|
|
|
view: displayNamesLabel)
|
|
|
|
mutableDisplayNames.resizeAttachments(toLineHeight: displayNamesLabel.font.lineHeight)
|
|
|
|
|
|
|
|
displayNamesLabel.attributedText = mutableDisplayNames
|
2021-01-27 03:42:22 +01:00
|
|
|
timeLabel.text = viewModel.statusViewModel?.time
|
2020-10-29 07:03:45 +01:00
|
|
|
statusBodyView.viewModel = viewModel.statusViewModel
|
|
|
|
avatarsView.viewModel = viewModel
|
|
|
|
}
|
|
|
|
}
|