From 79b1c531f0c70e95c76f6c2d509472bf4a3a56c1 Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Fri, 30 Oct 2020 17:29:43 -0700 Subject: [PATCH] Notifications tab --- Metatext.xcodeproj/project.pbxproj | 4 + View Controllers/TableViewController.swift | 2 +- .../ViewModels/CollectionItemsViewModel.swift | 1 + .../ViewModels/NotificationViewModel.swift | 20 +++ Views/NotificationView.swift | 106 ++++++++++++-- Views/Status/StatusBodyView.swift | 138 ++++++++++++++++++ Views/Status/StatusView.swift | 101 +------------ 7 files changed, 266 insertions(+), 106 deletions(-) create mode 100644 Views/Status/StatusBodyView.swift diff --git a/Metatext.xcodeproj/project.pbxproj b/Metatext.xcodeproj/project.pbxproj index 76ad6e7..61ea717 100644 --- a/Metatext.xcodeproj/project.pbxproj +++ b/Metatext.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA01254B6101009094DF /* NotificationListCell.swift */; }; D036AA07254B6118009094DF /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA06254B6118009094DF /* NotificationView.swift */; }; D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */; }; + D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA16254CA823009094DF /* StatusBodyView.swift */; }; D03B1B2A253818F3008F964B /* MediaPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B1B29253818F3008F964B /* MediaPreferencesView.swift */; }; D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04226FC2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift */; }; D0625E59250F092900502611 /* StatusListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E58250F092900502611 /* StatusListCell.swift */; }; @@ -124,6 +125,7 @@ D036AA01254B6101009094DF /* NotificationListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationListCell.swift; sourceTree = ""; }; D036AA06254B6118009094DF /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; D036AA0B254B612B009094DF /* NotificationContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentConfiguration.swift; sourceTree = ""; }; + D036AA16254CA823009094DF /* StatusBodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBodyView.swift; sourceTree = ""; }; D03B1B29253818F3008F964B /* MediaPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreferencesView.swift; sourceTree = ""; }; D04226FC2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupAndSyncingPreferencesView.swift; sourceTree = ""; }; D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -278,6 +280,7 @@ D01F41E224F8889700D55A2D /* StatusAttachmentsView.swift */, D0BEB1F224F8EE8C001B0F04 /* StatusAttachmentView.swift */, D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */, + D036AA16254CA823009094DF /* StatusBodyView.swift */, D0625E58250F092900502611 /* StatusListCell.swift */, D00CB2EC2533ACC00080096B /* StatusView.swift */, ); @@ -645,6 +648,7 @@ D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */, D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */, D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */, + D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */, D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */, D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */, D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */, diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index dca5466..908cc7b 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -391,7 +391,7 @@ private extension TableViewController { view.isDescendant(of: visibleView), let superview = view.superview, let attachmentsViewClosestToCenter = tableView.visibleCells - .compactMap({ ($0.contentView as? StatusView)?.attachmentsView }) + .compactMap({ ($0.contentView as? StatusView)?.bodyView.attachmentsView }) .filter(\.shouldAutoplay) .min(by: { abs(superview.convert($0.frame, from: $0.superview).midY - view.frame.midY) diff --git a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift index ebdcca3..74173f1 100644 --- a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift +++ b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift @@ -165,6 +165,7 @@ extension CollectionItemsViewModel: CollectionViewModel { } } + // swiftlint:disable:next function_body_length public func viewModel(indexPath: IndexPath) -> CollectionItemViewModel { let item = items.value[indexPath.section][indexPath.item] let cachedViewModel = viewModelCache[item]?.viewModel diff --git a/ViewModels/Sources/ViewModels/NotificationViewModel.swift b/ViewModels/Sources/ViewModels/NotificationViewModel.swift index 504e92c..e635bf5 100644 --- a/ViewModels/Sources/ViewModels/NotificationViewModel.swift +++ b/ViewModels/Sources/ViewModels/NotificationViewModel.swift @@ -7,6 +7,7 @@ import ServiceLayer public final class NotificationViewModel: CollectionItemViewModel, ObservableObject { public let accountViewModel: AccountViewModel + public let statusViewModel: StatusViewModel? public let events: AnyPublisher, Never> private let notificationService: NotificationService @@ -20,6 +21,15 @@ public final class NotificationViewModel: CollectionItemViewModel, ObservableObj accountService: notificationService.navigationService.accountService( account: notificationService.notification.account), identification: identification) + + if let status = notificationService.notification.status { + statusViewModel = StatusViewModel( + statusService: notificationService.navigationService.statusService(status: status), + identification: identification) + } else { + statusViewModel = nil + } + self.events = eventsSubject.eraseToAnyPublisher() } } @@ -28,4 +38,14 @@ public extension NotificationViewModel { var type: MastodonNotification.NotificationType { notificationService.notification.type } + + func accountSelected() { + eventsSubject.send( + Just(.navigation( + .profile( + notificationService.navigationService.profileService( + account: notificationService.notification.account)))) + .setFailureType(to: Error.self) + .eraseToAnyPublisher()) + } } diff --git a/Views/NotificationView.swift b/Views/NotificationView.swift index c197bd9..1175ada 100644 --- a/Views/NotificationView.swift +++ b/Views/NotificationView.swift @@ -1,11 +1,17 @@ // Copyright © 2020 Metabolist. All rights reserved. +import Kingfisher import Mastodon import UIKit -class NotificationView: UIView { +final class NotificationView: UIView { private let iconImageView = UIImageView() + private let avatarImageView = AnimatedImageView() + private let avatarButton = UIButton() private let typeLabel = UILabel() + private let displayNameLabel = UILabel() + private let accountLabel = UILabel() + private let statusBodyView = StatusBodyView() private var notificationConfiguration: NotificationContentConfiguration init(configuration: NotificationContentConfiguration) { @@ -37,31 +43,89 @@ extension NotificationView: UIContentView { } private extension NotificationView { + // swiftlint:disable function_body_length func initialSetup() { - let stackView = UIStackView() + let containerStackView = UIStackView() + let sideStackView = UIStackView() + let mainStackView = UIStackView() - addSubview(stackView) - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.spacing = .compactSpacing + addSubview(containerStackView) + containerStackView.translatesAutoresizingMaskIntoConstraints = false + containerStackView.spacing = .defaultSpacing - stackView.addArrangedSubview(iconImageView) + sideStackView.axis = .vertical + sideStackView.alignment = .trailing + sideStackView.spacing = .compactSpacing + sideStackView.addArrangedSubview(iconImageView) + sideStackView.addArrangedSubview(avatarImageView) + sideStackView.addArrangedSubview(UIView()) + containerStackView.addArrangedSubview(sideStackView) + + mainStackView.axis = .vertical + mainStackView.spacing = .compactSpacing + mainStackView.addArrangedSubview(typeLabel) + mainStackView.addSubview(UIView()) + mainStackView.addArrangedSubview(statusBodyView) + mainStackView.addArrangedSubview(displayNameLabel) + mainStackView.addArrangedSubview(accountLabel) + containerStackView.addArrangedSubview(mainStackView) + + iconImageView.contentMode = .scaleAspectFit iconImageView.setContentHuggingPriority(.required, for: .horizontal) - stackView.addArrangedSubview(typeLabel) - typeLabel.font = .preferredFont(forTextStyle: .body) + avatarImageView.layer.cornerRadius = .avatarDimension / 2 + avatarImageView.clipsToBounds = true + + let avatarHeightConstraint = avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension) + + avatarHeightConstraint.priority = .justBelowMax + + avatarButton.translatesAutoresizingMaskIntoConstraints = false + avatarImageView.addSubview(avatarButton) + avatarImageView.isUserInteractionEnabled = true + avatarButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted) + + avatarButton.addAction( + UIAction { [weak self] _ in self?.notificationConfiguration.viewModel.accountSelected() }, + for: .touchUpInside) + + typeLabel.font = .preferredFont(forTextStyle: .headline) typeLabel.adjustsFontForContentSizeCategory = true + typeLabel.numberOfLines = 0 + + statusBodyView.alpha = 0.5 + statusBodyView.isUserInteractionEnabled = false + + displayNameLabel.font = .preferredFont(forTextStyle: .headline) + displayNameLabel.adjustsFontForContentSizeCategory = true + displayNameLabel.numberOfLines = 0 + + accountLabel.font = .preferredFont(forTextStyle: .subheadline) + accountLabel.adjustsFontForContentSizeCategory = true + accountLabel.textColor = .secondaryLabel + accountLabel.numberOfLines = 0 NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor), - stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor), - stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor), - stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor) + containerStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor), + containerStackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor), + containerStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor), + containerStackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor), + avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension), + avatarHeightConstraint, + sideStackView.widthAnchor.constraint(equalToConstant: .avatarDimension), + iconImageView.centerYAnchor.constraint(equalTo: typeLabel.centerYAnchor), + avatarButton.leadingAnchor.constraint(equalTo: avatarImageView.leadingAnchor), + avatarButton.topAnchor.constraint(equalTo: avatarImageView.topAnchor), + avatarButton.bottomAnchor.constraint(equalTo: avatarImageView.bottomAnchor), + avatarButton.trailingAnchor.constraint(equalTo: avatarImageView.trailingAnchor) ]) } func applyNotificationConfiguration() { let viewModel = notificationConfiguration.viewModel + avatarImageView.kf.setImage(with: viewModel.accountViewModel.avatarURL()) + switch viewModel.type { case .follow: typeLabel.attributedText = "notifications.followed-you".localizedBolding( @@ -96,10 +160,28 @@ private extension NotificationView { iconImageView.tintColor = nil } + if viewModel.statusViewModel == nil { + let mutableDisplayName = NSMutableAttributedString(string: viewModel.accountViewModel.displayName) + + mutableDisplayName.insert(emoji: viewModel.accountViewModel.emoji, view: displayNameLabel) + mutableDisplayName.resizeAttachments(toLineHeight: displayNameLabel.font.lineHeight) + displayNameLabel.attributedText = mutableDisplayName + accountLabel.text = viewModel.accountViewModel.accountName + statusBodyView.isHidden = true + displayNameLabel.isHidden = false + accountLabel.isHidden = false + } else { + statusBodyView.viewModel = viewModel.statusViewModel + statusBodyView.isHidden = false + displayNameLabel.isHidden = true + accountLabel.isHidden = true + } + iconImageView.image = UIImage( systemName: viewModel.type.systemImageName, withConfiguration: UIImage.SymbolConfiguration(scale: .medium)) } + // swiftlint:enable function_body_length } extension MastodonNotification.NotificationType { diff --git a/Views/Status/StatusBodyView.swift b/Views/Status/StatusBodyView.swift new file mode 100644 index 0000000..57fed3a --- /dev/null +++ b/Views/Status/StatusBodyView.swift @@ -0,0 +1,138 @@ +// Copyright © 2020 Metabolist. All rights reserved. + +import UIKit +import ViewModels + +final class StatusBodyView: UIView { + let spoilerTextLabel = UILabel() + let toggleShowContentButton = UIButton(type: .system) + let contentTextView = TouchFallthroughTextView() + let attachmentsView = StatusAttachmentsView() + let pollView = PollView() + let cardView = CardView() + + var viewModel: StatusViewModel? { + didSet { + guard let viewModel = viewModel else { return } + + let isContextParent = viewModel.configuration.isContextParent + let mutableContent = NSMutableAttributedString(attributedString: viewModel.content) + let mutableSpoilerText = NSMutableAttributedString(string: viewModel.spoilerText) + let contentFont = UIFont.preferredFont(forTextStyle: isContextParent ? .title3 : .callout) + let contentRange = NSRange(location: 0, length: mutableContent.length) + + contentTextView.shouldFallthrough = !isContextParent + + mutableContent.removeAttribute(.font, range: contentRange) + mutableContent.addAttributes( + [.font: contentFont, .foregroundColor: UIColor.label], + range: contentRange) + mutableContent.insert(emoji: viewModel.contentEmoji, view: contentTextView) + mutableContent.resizeAttachments(toLineHeight: contentFont.lineHeight) + contentTextView.attributedText = mutableContent + contentTextView.isHidden = contentTextView.text == "" + + mutableSpoilerText.insert(emoji: viewModel.contentEmoji, view: spoilerTextLabel) + mutableSpoilerText.resizeAttachments(toLineHeight: spoilerTextLabel.font.lineHeight) + spoilerTextLabel.font = contentFont + spoilerTextLabel.attributedText = mutableSpoilerText + spoilerTextLabel.isHidden = spoilerTextLabel.text == "" + toggleShowContentButton.setTitle( + viewModel.shouldShowContent + ? NSLocalizedString("status.show-less", comment: "") + : NSLocalizedString("status.show-more", comment: ""), + for: .normal) + toggleShowContentButton.isHidden = viewModel.spoilerText == "" + + contentTextView.isHidden = !viewModel.shouldShowContent + + attachmentsView.isHidden = viewModel.attachmentViewModels.count == 0 + attachmentsView.viewModel = viewModel + + pollView.isHidden = viewModel.pollOptions.count == 0 + pollView.viewModel = viewModel + + cardView.viewModel = viewModel.cardViewModel + cardView.isHidden = viewModel.cardViewModel == nil + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + + initialSetup() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension StatusBodyView: UITextViewDelegate { + func textView( + _ textView: UITextView, + shouldInteractWith URL: URL, + in characterRange: NSRange, + interaction: UITextItemInteraction) -> Bool { + switch interaction { + case .invokeDefaultAction: + viewModel?.urlSelected(URL) + return false + case .preview: return false + case .presentActions: return false + @unknown default: return false + } + } +} + +private extension StatusBodyView { + func initialSetup() { + let stackView = UIStackView() + + addSubview(stackView) + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + stackView.spacing = .compactSpacing + + spoilerTextLabel.numberOfLines = 0 + spoilerTextLabel.adjustsFontForContentSizeCategory = true + stackView.addArrangedSubview(spoilerTextLabel) + + toggleShowContentButton.titleLabel?.font = .preferredFont(forTextStyle: .headline) + toggleShowContentButton.titleLabel?.adjustsFontForContentSizeCategory = true + toggleShowContentButton.addAction( + UIAction { [weak self] _ in self?.viewModel?.toggleShowContent() }, + for: .touchUpInside) + stackView.addArrangedSubview(toggleShowContentButton) + + contentTextView.adjustsFontForContentSizeCategory = true + contentTextView.isScrollEnabled = false + contentTextView.backgroundColor = .clear + contentTextView.delegate = self + stackView.addArrangedSubview(contentTextView) + + stackView.addArrangedSubview(attachmentsView) + + stackView.addArrangedSubview(pollView) + + cardView.button.addAction( + UIAction { [weak self] _ in + guard + let viewModel = self?.viewModel, + let url = viewModel.cardViewModel?.url + else { return } + + viewModel.urlSelected(url) + }, + for: .touchUpInside) + stackView.addArrangedSubview(cardView) + + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: leadingAnchor), + stackView.topAnchor.constraint(equalTo: topAnchor), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + } +} diff --git a/Views/Status/StatusView.swift b/Views/Status/StatusView.swift index 6dfa7ad..7263652 100644 --- a/Views/Status/StatusView.swift +++ b/Views/Status/StatusView.swift @@ -12,12 +12,7 @@ final class StatusView: UIView { let displayNameLabel = UILabel() let accountLabel = UILabel() let timeLabel = UILabel() - let spoilerTextLabel = UILabel() - let toggleShowContentButton = UIButton(type: .system) - let contentTextView = TouchFallthroughTextView() - let attachmentsView = StatusAttachmentsView() - let pollView = PollView() - let cardView = CardView() + let bodyView = StatusBodyView() let contextParentTimeLabel = UILabel() let timeApplicationDividerLabel = UILabel() let applicationButton = UIButton(type: .system) @@ -145,38 +140,7 @@ private extension StatusView { nameAccountContainerStackView.addArrangedSubview(nameAccountTimeStackView) mainStackView.addArrangedSubview(nameAccountContainerStackView) - spoilerTextLabel.numberOfLines = 0 - spoilerTextLabel.adjustsFontForContentSizeCategory = true - mainStackView.addArrangedSubview(spoilerTextLabel) - - toggleShowContentButton.titleLabel?.font = .preferredFont(forTextStyle: .headline) - toggleShowContentButton.titleLabel?.adjustsFontForContentSizeCategory = true - toggleShowContentButton.addAction( - UIAction { [weak self] _ in self?.statusConfiguration.viewModel.toggleShowContent() }, - for: .touchUpInside) - mainStackView.addArrangedSubview(toggleShowContentButton) - - contentTextView.adjustsFontForContentSizeCategory = true - contentTextView.isScrollEnabled = false - contentTextView.backgroundColor = .clear - contentTextView.delegate = self - mainStackView.addArrangedSubview(contentTextView) - - mainStackView.addArrangedSubview(attachmentsView) - - mainStackView.addArrangedSubview(pollView) - - cardView.button.addAction( - UIAction { [weak self] _ in - guard - let viewModel = self?.statusConfiguration.viewModel, - let url = viewModel.cardViewModel?.url - else { return } - - viewModel.urlSelected(url) - }, - for: .touchUpInside) - mainStackView.addArrangedSubview(cardView) + mainStackView.addArrangedSubview(bodyView) contextParentTimeLabel.font = .preferredFont(forTextStyle: .footnote) contextParentTimeLabel.adjustsFontForContentSizeCategory = true @@ -296,15 +260,10 @@ private extension StatusView { func applyStatusConfiguration() { let viewModel = statusConfiguration.viewModel let isContextParent = viewModel.configuration.isContextParent - let mutableContent = NSMutableAttributedString(attributedString: viewModel.content) let mutableDisplayName = NSMutableAttributedString(string: viewModel.displayName) - let mutableSpoilerText = NSMutableAttributedString(string: viewModel.spoilerText) - let contentFont = UIFont.preferredFont(forTextStyle: isContextParent ? .title3 : .callout) - let contentRange = NSRange(location: 0, length: mutableContent.length) avatarImageView.kf.setImage(with: viewModel.avatarURL) - contentTextView.shouldFallthrough = !isContextParent sideStackView.isHidden = isContextParent avatarImageView.removeFromSuperview() @@ -326,25 +285,11 @@ private extension StatusView { inReplyToView.isHidden = !viewModel.configuration.isReplyInContext hasReplyFollowingView.isHidden = !viewModel.configuration.hasReplyFollowing - if - viewModel.isReblog { - let metaText = String.localizedStringWithFormat( - NSLocalizedString("status.reblogged-by", comment: ""), - viewModel.rebloggedByDisplayName) - let mutableInfoText = NSMutableAttributedString(string: metaText) - - let range = (mutableInfoText.string as NSString).range(of: viewModel.rebloggedByDisplayName) - - if range.location != NSNotFound, - let boldFontDescriptor = infoLabel.font.fontDescriptor.withSymbolicTraits([.traitBold]) { - let boldFont = UIFont(descriptor: boldFontDescriptor, size: infoLabel.font.pointSize) - - mutableInfoText.setAttributes([NSAttributedString.Key.font: boldFont], range: range) - } - - mutableInfoText.insert(emoji: viewModel.rebloggedByDisplayNameEmoji, view: infoLabel) - mutableInfoText.resizeAttachments(toLineHeight: infoLabel.font.lineHeight) - infoLabel.attributedText = mutableInfoText + if viewModel.isReblog { + infoLabel.attributedText = "status.reblogged-by".localizedBolding( + displayName: viewModel.rebloggedByDisplayName, + emoji: viewModel.rebloggedByDisplayNameEmoji, + label: infoLabel) infoIcon.image = UIImage( systemName: "arrow.2.squarepath", withConfiguration: UIImage.SymbolConfiguration(scale: .small)) @@ -362,33 +307,10 @@ private extension StatusView { infoIcon.isHidden = true } - mutableContent.removeAttribute(.font, range: contentRange) - mutableContent.addAttributes( - [.font: contentFont, .foregroundColor: UIColor.label], - range: contentRange) - mutableContent.insert(emoji: viewModel.contentEmoji, view: contentTextView) - mutableContent.resizeAttachments(toLineHeight: contentFont.lineHeight) - contentTextView.attributedText = mutableContent - contentTextView.isHidden = contentTextView.text == "" - mutableDisplayName.insert(emoji: viewModel.displayNameEmoji, view: displayNameLabel) mutableDisplayName.resizeAttachments(toLineHeight: displayNameLabel.font.lineHeight) displayNameLabel.attributedText = mutableDisplayName - mutableSpoilerText.insert(emoji: viewModel.contentEmoji, view: spoilerTextLabel) - mutableSpoilerText.resizeAttachments(toLineHeight: spoilerTextLabel.font.lineHeight) - spoilerTextLabel.font = contentFont - spoilerTextLabel.attributedText = mutableSpoilerText - spoilerTextLabel.isHidden = spoilerTextLabel.text == "" - toggleShowContentButton.setTitle( - viewModel.shouldShowContent - ? NSLocalizedString("status.show-less", comment: "") - : NSLocalizedString("status.show-more", comment: ""), - for: .normal) - toggleShowContentButton.isHidden = viewModel.spoilerText == "" - - contentTextView.isHidden = !viewModel.shouldShowContent - nameAccountTimeStackView.axis = isContextParent ? .vertical : .horizontal nameAccountTimeStackView.alignment = isContextParent ? .leading : .fill nameAccountTimeStackView.spacing = isContextParent ? 0 : .compactSpacing @@ -407,14 +329,7 @@ private extension StatusView { timeLabel.text = viewModel.time timeLabel.isHidden = isContextParent - attachmentsView.isHidden = viewModel.attachmentViewModels.count == 0 - attachmentsView.viewModel = viewModel - - pollView.isHidden = viewModel.pollOptions.count == 0 - pollView.viewModel = viewModel - - cardView.viewModel = viewModel.cardViewModel - cardView.isHidden = viewModel.cardViewModel == nil + bodyView.viewModel = viewModel contextParentTimeLabel.text = viewModel.contextParentTime timeApplicationDividerLabel.isHidden = viewModel.applicationName == nil