Show author in Card (IOS-284) (#1321)

This PR updates the Preview Card. Apart from a minor background color
change, it introduces the new `StatusCardAuthorControl`, which is
visible if the author of an article has a Mastodon-account. If the user
taps on the account, the given profile will open.

This feature will be available in Mastodon >4.3 (See [this
PR](https://github.com/mastodon/mastodon/pull/30398), for example). HMI,
if you need more information on how to test this etc.

Please enjoy these screenshots with some fake data:


![1321](https://github.com/mastodon/mastodon-ios/assets/2580019/cc580c84-6700-4f15-b1b0-b845c21a445e)
This commit is contained in:
Nathan Mattes 2024-06-30 22:35:09 +02:00 committed by GitHub
commit 4fdb1dd4a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 307 additions and 44 deletions

View File

@ -232,6 +232,10 @@
"edit_history": {
"title": "Edit History",
"original_post": "Original Post · %s"
},
"card": {
"by": "By",
"by_author": "By %s"
}
},
"friendship": {
@ -751,6 +755,7 @@
"title": "Settings",
"general": "General",
"notifications": "Notifications",
"privacy_safety": "Privacy & Safety",
"support_mastodon": "Support Mastodon",
"about_mastodon": "About Mastodon",
"server_details": "Server Details",
@ -821,6 +826,25 @@
"go_to_settings": "Go to Notification Settings"
}
},
"privacy_safety": {
"title": "Privacy & Safety",
"preset": {
"title": "Preset",
"open_and_public": "Open & Public",
"private_and_restricted": "Private & Restricted",
"custom": "Custom"
},
"default_post_visibility": {
"title": "Default Post Visibility",
"public": "Public",
"followers_only": "Followers Only",
"only_people_mentioned": "Only People Mentioned"
},
"manually_approve_follow_requests": "Manually Approve Follow Requests",
"show_followers_and_following": "Show Followers & Following",
"suggest_my_account_to_others": "Suggest My Account to Others",
"appear_in_search_engines": "Appear in Search Engines"
}
},
"report": {

View File

@ -232,6 +232,10 @@
"edit_history": {
"title": "Edit History",
"original_post": "Original Post · %s"
},
"card": {
"by": "By",
"by_author": "By %s"
}
},
"friendship": {

View File

@ -1600,7 +1600,6 @@
children = (
5D03938E2612D200007FE196 /* Webview */,
DB68A04F25E9028800CFDF14 /* NavigationController */,
DB9D6C2025E502C60051B173 /* ViewModel */,
2D7631A525C1532D00929FB9 /* View */,
DBA5E7A6263BD298004598BB /* ContextMenu */,
);
@ -2697,13 +2696,6 @@
path = Profile;
sourceTree = "<group>";
};
DB9D6C2025E502C60051B173 /* ViewModel */ = {
isa = PBXGroup;
children = (
);
path = ViewModel;
sourceTree = "<group>";
};
DB9F58ED26EF435800E7BBE9 /* Account */ = {
isa = PBXGroup;
children = (

View File

@ -144,6 +144,17 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
}
}
func tableViewCell(
_ cell: UITableViewCell,
statusView: StatusView,
cardControl: StatusCardControl,
didTapProfile account: Mastodon.Entity.Account
) {
Task {
await DataSourceFacade.coordinateToProfileScene(provider:self, account: account)
}
}
func tableViewCell(
_ cell: UITableViewCell,
statusView: StatusView,

View File

@ -491,8 +491,9 @@ extension NotificationView {
// MARK: - StatusViewDelegate
extension NotificationView: StatusViewDelegate {
public func statusView(_ statusView: StatusView, didTapCardWithURL url: URL) {
assertionFailure()
public func statusView(_ statusView: StatusView, didTapCardWithURL url: URL) { assertionFailure() }
public func statusView(_ statusView: StatusView, cardControl: StatusCardControl, didTapProfile account: Mastodon.Entity.Account) {
// no op
}
public func statusView(_ statusView: StatusView, headerDidPressed header: UIView) {

View File

@ -40,6 +40,7 @@ protocol StatusTableViewCellDelegate: AnyObject, AutoGenerateProtocolDelegate {
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, favoriteButtonDidPressed button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, showEditHistory button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, cardControl: StatusCardControl, didTapURL url: URL)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, cardControl: StatusCardControl, didTapProfile account: Mastodon.Entity.Account)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, cardControlMenu: StatusCardControl) -> [LabeledAction]?
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, accessibilityActivate: Void)
// sourcery:end
@ -114,6 +115,10 @@ extension StatusViewDelegate where Self: StatusViewContainerTableViewCell {
delegate?.tableViewCell(self, statusView: statusView, cardControl: cardControl, didTapURL: url)
}
func statusView(_ statusView: StatusView, cardControl: StatusCardControl, didTapProfile account: Mastodon.Entity.Account) {
delegate?.tableViewCell(self, statusView: statusView, cardControl: cardControl, didTapProfile: account)
}
func statusView(_ statusView: StatusView, cardControlMenu: StatusCardControl) -> [LabeledAction]? {
return delegate?.tableViewCell(self, statusView: statusView, cardControlMenu: cardControlMenu)
}

View File

@ -411,6 +411,14 @@ public enum L10n {
/// Boosts
public static let reblogsTitle = L10n.tr("Localizable", "Common.Controls.Status.Buttons.ReblogsTitle", fallback: "Boosts")
}
public enum Card {
/// By
public static let by = L10n.tr("Localizable", "Common.Controls.Status.Card.By", fallback: "By")
/// By %@
public static func byAuthor(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.Card.ByAuthor", String(describing: p1), fallback: "By %@")
}
}
public enum EditHistory {
/// Original Post · %@
public static func originalPost(_ p1: Any) -> String {

View File

@ -136,6 +136,8 @@ Please check your internet connection.";
"Common.Controls.Status.Buttons.EditHistoryTitle" = "Edit History";
"Common.Controls.Status.Buttons.FavoritesTitle" = "Favorites";
"Common.Controls.Status.Buttons.ReblogsTitle" = "Boosts";
"Common.Controls.Status.Card.By" = "By";
"Common.Controls.Status.Card.ByAuthor" = "By %@";
"Common.Controls.Status.ContentWarning" = "Content Warning";
"Common.Controls.Status.EditHistory.OriginalPost" = "Original Post · %@";
"Common.Controls.Status.EditHistory.Title" = "Edit History";
@ -562,19 +564,19 @@ If you disagree with the policy for **%@**, you can go back and pick a different
"Scene.Settings.Overview.ServerDetails" = "Server Details";
"Scene.Settings.Overview.SupportMastodon" = "Support Mastodon";
"Scene.Settings.Overview.Title" = "Settings";
"Scene.Settings.PrivacySafety.Title" = "Privacy & Safety";
"Scene.Settings.PrivacySafety.Preset.Title" = "Preset";
"Scene.Settings.PrivacySafety.Preset.OpenAndPublic" = "Open & Public";
"Scene.Settings.PrivacySafety.Preset.PrivateAndRestricted" = "Private & Restricted";
"Scene.Settings.PrivacySafety.Preset.Custom" = "Custom";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.Title" = "Default Post Visibility";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.Public" = "Public";
"Scene.Settings.PrivacySafety.AppearInSearchEngines" = "Appear in Search Engines";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.FollowersOnly" = "Followers Only";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.OnlyPeopleMentioned" = "Only People Mentioned";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.Public" = "Public";
"Scene.Settings.PrivacySafety.DefaultPostVisibility.Title" = "Default Post Visibility";
"Scene.Settings.PrivacySafety.ManuallyApproveFollowRequests" = "Manually Approve Follow Requests";
"Scene.Settings.PrivacySafety.Preset.Custom" = "Custom";
"Scene.Settings.PrivacySafety.Preset.OpenAndPublic" = "Open & Public";
"Scene.Settings.PrivacySafety.Preset.PrivateAndRestricted" = "Private & Restricted";
"Scene.Settings.PrivacySafety.Preset.Title" = "Preset";
"Scene.Settings.PrivacySafety.ShowFollowersAndFollowing" = "Show Followers & Following";
"Scene.Settings.PrivacySafety.SuggestMyAccountToOthers" = "Suggest My Account to Others";
"Scene.Settings.PrivacySafety.AppearInSearchEngines" = "Appear in Search Engines";
"Scene.Settings.PrivacySafety.Title" = "Privacy & Safety";
"Scene.Settings.ServerDetails.About" = "About";
"Scene.Settings.ServerDetails.AboutInstance.LegalNotice" = "A legal notice";
"Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin";
@ -615,4 +617,4 @@ If you disagree with the policy for **%@**, you can go back and pick a different
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
"Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social";
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";

View File

@ -22,8 +22,10 @@ extension Mastodon.Entity {
public let title: String
public let description: String
public let type: Type
@available(*, deprecated, message: "Use authors-array. Kept for compatibility")
public let authorName: String?
@available(*, deprecated, message: "Use authors-array. Kept for compatibility")
public let authorURL: String?
public let providerName: String?
public let providerURL: String?
@ -33,7 +35,9 @@ extension Mastodon.Entity {
public let image: String?
public let embedURL: String?
public let blurhash: String?
public let authors: [Mastodon.Entity.Card.Author]?
public let publishedAt: Date?
enum CodingKeys: String, CodingKey {
case url
case title
@ -49,10 +53,20 @@ extension Mastodon.Entity {
case image
case embedURL = "embed_url"
case blurhash
case authors
case publishedAt = "published_at"
}
}
}
extension Mastodon.Entity.Card {
public struct Author: Codable, Sendable {
public let name: String?
public let url: String?
public let account: Mastodon.Entity.Account?
}
}
extension Mastodon.Entity.Card {
public enum `Type`: RawRepresentable, Codable, Sendable {
case link

View File

@ -27,7 +27,11 @@ extension Date {
formatter.timeStyle = .none // none
return formatter
}()
public var abbreviatedDate: String {
return Date.abbreviatedDateFormatter.string(from: self)
}
public var localizedAbbreviatedSlowedTimeAgoSinceNow: String {
return Date.relativeTimestampFormatter.localizedString(for: self, relativeTo: Date())
}

View File

@ -0,0 +1,56 @@
import UIKit
import MastodonAsset
import MastodonSDK
class StatusCardAuthorControl: UIControl {
let authorLabel: UILabel
let avatarImage: AvatarImageView
private let contentStackView: UIStackView
public override init(frame: CGRect) {
authorLabel = UILabel()
authorLabel.textAlignment = .center
authorLabel.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 16, weight: .bold))
authorLabel.isUserInteractionEnabled = false
avatarImage = AvatarImageView()
avatarImage.translatesAutoresizingMaskIntoConstraints = false
avatarImage.configure(cornerConfiguration: AvatarImageView.CornerConfiguration(corner: .fixed(radius: 4)))
avatarImage.isUserInteractionEnabled = false
contentStackView = UIStackView(arrangedSubviews: [avatarImage, authorLabel])
contentStackView.alignment = .center
contentStackView.spacing = 6
contentStackView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.layoutMargins = UIEdgeInsets(horizontal: 6, vertical: 8)
contentStackView.isUserInteractionEnabled = false
super.init(frame: frame)
addSubview(contentStackView)
setupConstraints()
backgroundColor = Asset.Colors.Button.userFollowing.color
layer.cornerRadius = 10
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
private func setupConstraints() {
let constraints = [
contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
contentStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 6),
trailingAnchor.constraint(equalTo: contentStackView.trailingAnchor, constant: 6),
bottomAnchor.constraint(equalTo: contentStackView.bottomAnchor, constant: 6),
avatarImage.widthAnchor.constraint(equalToConstant: 16),
avatarImage.widthAnchor.constraint(equalTo: avatarImage.heightAnchor),
]
NSLayoutConstraint.activate(constraints)
}
public func configure(with account: Mastodon.Entity.Account) {
authorLabel.text = account.displayNameWithFallback
avatarImage.configure(with: account.avatarImageURL())
}
}

View File

@ -17,6 +17,7 @@ import MastodonSDK
public protocol StatusCardControlDelegate: AnyObject {
func statusCardControl(_ statusCardControl: StatusCardControl, didTapURL url: URL)
func statusCardControl(_ statusCardControl: StatusCardControl, didTapAuthor author: Mastodon.Entity.Account)
func statusCardControlMenu(_ statusCardControl: StatusCardControl) -> [LabeledAction]?
}
@ -26,13 +27,20 @@ public final class StatusCardControl: UIControl {
private var disposeBag = Set<AnyCancellable>()
private let containerStackView = UIStackView()
private let headerContentStackView = UIStackView()
private let labelStackView = UIStackView()
private let highlightView = UIView()
private let dividerView = UIView()
private let imageView = UIImageView()
private let publisherDateStackView: UIStackView
private let publisherLabel = UILabel()
private let publisherDateSeparaturLabel = UILabel()
private let dateLabel = UILabel()
private let titleLabel = UILabel()
private let linkLabel = UILabel()
private let descriptionLabel = UILabel()
private lazy var showEmbedButton: UIButton = {
var configuration = UIButton.Configuration.gray()
configuration.background.visualEffect = UIBlurEffect(style: .systemUltraThinMaterial)
@ -48,12 +56,24 @@ public final class StatusCardControl: UIControl {
}()
private var html = ""
private let authorDivider: UIView
private let mastodonLogoImageView: UIImageView
private let byLabel: UILabel
private let authorLabel: UILabel
private let authorAccountButton: StatusCardAuthorControl
private let authorStackView: UIStackView
private static let cardContentPool = WKProcessPool()
private var webView: WKWebView?
private var layout: Layout?
private var layoutConstraints: [NSLayoutConstraint] = []
private var dividerConstraint: NSLayoutConstraint?
private var authorDividerConstraint: NSLayoutConstraint?
private var author: Mastodon.Entity.Account?
private var url: URL?
public override var isHighlighted: Bool {
didSet {
@ -68,6 +88,54 @@ public final class StatusCardControl: UIControl {
}
public override init(frame: CGRect) {
let mastodonLogo = Asset.Scene.Sidebar.logo.image.withRenderingMode(.alwaysTemplate)
mastodonLogoImageView = UIImageView(image: mastodonLogo)
mastodonLogoImageView.tintColor = .gray
mastodonLogoImageView.translatesAutoresizingMaskIntoConstraints = false
byLabel = UILabel()
byLabel.text = L10n.Common.Controls.Status.Card.by
byLabel.numberOfLines = 1
byLabel.textColor = .secondaryLabel
byLabel.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
authorLabel = UILabel()
authorLabel.numberOfLines = 1
authorLabel.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
authorLabel.textColor = .secondaryLabel
publisherLabel.numberOfLines = 1
publisherLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
publisherLabel.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
publisherLabel.textColor = .secondaryLabel
publisherDateSeparaturLabel.numberOfLines = 1
publisherDateSeparaturLabel.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
publisherDateSeparaturLabel.textColor = .secondaryLabel
publisherDateSeparaturLabel.text = "·"
dateLabel.numberOfLines = 1
dateLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
dateLabel.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
dateLabel.textColor = .secondaryLabel
publisherDateStackView = UIStackView(arrangedSubviews: [publisherLabel, publisherDateSeparaturLabel, dateLabel, UIView()])
publisherDateStackView.axis = .horizontal
publisherDateStackView.alignment = .firstBaseline
publisherDateStackView.spacing = 3
authorAccountButton = StatusCardAuthorControl()
authorStackView = UIStackView(arrangedSubviews: [mastodonLogoImageView, byLabel, authorLabel, authorAccountButton, UIView()])
authorStackView.alignment = .center
authorStackView.layoutMargins = .init(top: 10, left: 16, bottom: 10, right: 16)
authorStackView.isLayoutMarginsRelativeArrangement = true
authorStackView.spacing = 8
authorStackView.isUserInteractionEnabled = true
authorDivider = UIView.separatorLine
super.init(frame: frame)
applyBranding()
@ -82,11 +150,11 @@ public final class StatusCardControl: UIControl {
titleLabel.numberOfLines = 2
titleLabel.textColor = Asset.Colors.Label.primary.color
titleLabel.font = .preferredFont(forTextStyle: .body)
titleLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .bold))
linkLabel.numberOfLines = 1
linkLabel.textColor = Asset.Colors.Label.secondary.color
linkLabel.font = .preferredFont(forTextStyle: .subheadline)
descriptionLabel.numberOfLines = 2
descriptionLabel.textColor = Asset.Colors.Label.secondary.color
descriptionLabel.font = .preferredFont(forTextStyle: .subheadline)
imageView.tintColor = Asset.Colors.Label.secondary.color
imageView.contentMode = .scaleAspectFill
@ -96,17 +164,26 @@ public final class StatusCardControl: UIControl {
imageView.setContentCompressionResistancePriority(.zero, for: .horizontal)
imageView.setContentCompressionResistancePriority(.zero, for: .vertical)
labelStackView.addArrangedSubview(publisherDateStackView)
labelStackView.addArrangedSubview(titleLabel)
labelStackView.addArrangedSubview(linkLabel)
labelStackView.layoutMargins = .init(top: 10, left: 10, bottom: 10, right: 10)
labelStackView.addArrangedSubview(descriptionLabel)
labelStackView.layoutMargins = .init(top: 16, left: 16, bottom: 16, right: 16)
labelStackView.isLayoutMarginsRelativeArrangement = true
labelStackView.isUserInteractionEnabled = false
labelStackView.axis = .vertical
labelStackView.spacing = 2
containerStackView.addArrangedSubview(imageView)
containerStackView.addArrangedSubview(dividerView)
containerStackView.addArrangedSubview(labelStackView)
containerStackView.isUserInteractionEnabled = false
headerContentStackView.addArrangedSubview(imageView)
headerContentStackView.addArrangedSubview(dividerView)
headerContentStackView.addArrangedSubview(labelStackView)
headerContentStackView.isUserInteractionEnabled = true
headerContentStackView.axis = .vertical
headerContentStackView.spacing = 2
headerContentStackView.setCustomSpacing(0, after: imageView)
containerStackView.addArrangedSubview(headerContentStackView)
containerStackView.addArrangedSubview(authorDivider)
containerStackView.addArrangedSubview(authorStackView)
containerStackView.distribution = .fill
addSubview(containerStackView)
@ -128,6 +205,7 @@ public final class StatusCardControl: UIControl {
addInteraction(UIContextMenuInteraction(delegate: self))
isAccessibilityElement = true
accessibilityTraits.insert(.link)
backgroundColor = .tertiarySystemFill
}
required init?(coder: NSCoder) {
@ -137,14 +215,60 @@ public final class StatusCardControl: UIControl {
public func configure(card: Mastodon.Entity.Card) {
let title = card.title.trimmingCharacters(in: .whitespacesAndNewlines)
let url = URL(string: card.url)
self.url = url
if let host = url?.host {
accessibilityLabel = "\(title) \(host)"
} else {
accessibilityLabel = title
}
if let providerName = card.providerName {
if let formattedPublishedDate = card.publishedAt?.abbreviatedDate {
dateLabel.text = formattedPublishedDate
publisherDateSeparaturLabel.isHidden = false
} else {
dateLabel.isHidden = true
publisherDateSeparaturLabel.isHidden = true
}
publisherLabel.text = providerName
publisherDateStackView.isHidden = false
} else {
publisherDateStackView.isHidden = true
}
if let author = card.authors?.first, let account = author.account {
authorAccountButton.configure(with: account)
authorAccountButton.isHidden = false
authorLabel.isHidden = true
byLabel.isHidden = false
mastodonLogoImageView.isHidden = false
self.author = account
authorAccountButton.addTarget(self, action: #selector(StatusCardControl.profileTapped(_:)), for: .touchUpInside)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(StatusCardControl.contentTapped(_:)))
headerContentStackView.addGestureRecognizer(tapGestureRecognizer)
} else {
if let author = card.authors?.first, let authorName = author.name, authorName.isEmpty == false {
authorLabel.text = L10n.Common.Controls.Status.Card.byAuthor(authorName)
} else if let authorName = card.authorName, authorName.isEmpty == false {
authorLabel.text = L10n.Common.Controls.Status.Card.byAuthor(authorName)
} else {
authorLabel.text = url?.host
}
author = nil
authorLabel.isHidden = false
byLabel.isHidden = true
mastodonLogoImageView.isHidden = true
authorAccountButton.isHidden = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(StatusCardControl.contentTapped(_:)))
addGestureRecognizer(tapGestureRecognizer)
}
titleLabel.text = title
linkLabel.text = url?.host
descriptionLabel.text = card.description
imageView.contentMode = .center
imageView.sd_setImage(
@ -178,9 +302,9 @@ public final class StatusCardControl: UIControl {
public override func didMoveToWindow() {
super.didMoveToWindow()
if let window = window {
layer.borderWidth = window.screen.pixelSize
if let window {
dividerConstraint?.constant = window.screen.pixelSize
authorDividerConstraint?.constant = window.screen.pixelSize
}
}
@ -190,6 +314,7 @@ public final class StatusCardControl: UIControl {
NSLayoutConstraint.deactivate(layoutConstraints)
dividerConstraint?.deactivate()
authorDividerConstraint?.deactivate()
let pixelSize = (window?.screen.pixelSize ?? 1)
switch layout {
@ -209,6 +334,7 @@ public final class StatusCardControl: UIControl {
.constraint(lessThanOrEqualToConstant: 400),
]
dividerConstraint = dividerView.heightAnchor.constraint(equalToConstant: pixelSize).activate()
authorDividerConstraint = authorDivider.heightAnchor.constraint(equalToConstant: pixelSize).activate()
case .compact:
containerStackView.alignment = .center
containerStackView.axis = .horizontal
@ -218,10 +344,17 @@ public final class StatusCardControl: UIControl {
heightAnchor.constraint(equalToConstant: 85).priority(.defaultLow - 1),
heightAnchor.constraint(greaterThanOrEqualToConstant: 85),
dividerView.heightAnchor.constraint(equalTo: containerStackView.heightAnchor),
authorDivider.heightAnchor.constraint(equalTo: containerStackView.heightAnchor),
]
dividerConstraint = dividerView.widthAnchor.constraint(equalToConstant: pixelSize).activate()
authorDividerConstraint = authorDivider.widthAnchor.constraint(equalToConstant: pixelSize).activate()
}
layoutConstraints.append(contentsOf: [
mastodonLogoImageView.widthAnchor.constraint(equalToConstant: 20),
mastodonLogoImageView.heightAnchor.constraint(equalTo: mastodonLogoImageView.widthAnchor),
])
NSLayoutConstraint.activate(layoutConstraints)
}
@ -236,7 +369,6 @@ public final class StatusCardControl: UIControl {
}
private func applyBranding() {
layer.borderColor = SystemTheme.separator.cgColor
dividerView.backgroundColor = SystemTheme.separator
imageView.backgroundColor = UIColor.tertiarySystemFill
}
@ -247,6 +379,18 @@ public final class StatusCardControl: UIControl {
}
set {}
}
@objc private func profileTapped(_ sender: UIButton) {
guard let author else { return }
delegate?.statusCardControl(self, didTapAuthor: author)
}
@objc private func contentTapped(_ sender: Any) {
guard let url else { return }
delegate?.statusCardControl(self, didTapURL: url)
}
}
// MARK: WKWebView delegates

View File

@ -35,6 +35,7 @@ public protocol StatusViewDelegate: AnyObject {
func statusView(_ statusView: StatusView, statusMetricView: StatusMetricView, favoriteButtonDidPressed button: UIButton)
func statusView(_ statusView: StatusView, statusMetricView: StatusMetricView, showEditHistory button: UIButton)
func statusView(_ statusView: StatusView, cardControl: StatusCardControl, didTapURL url: URL)
func statusView(_ statusView: StatusView, cardControl: StatusCardControl, didTapProfile account: Mastodon.Entity.Account)
func statusView(_ statusView: StatusView, cardControlMenu: StatusCardControl) -> [LabeledAction]?
// a11y
@ -373,7 +374,6 @@ extension StatusView {
contentMetaText.textView.linkDelegate = self
// card
statusCardControl.addTarget(self, action: #selector(statusCardControlPressed), for: .touchUpInside)
statusCardControl.delegate = self
// media
@ -410,12 +410,6 @@ extension StatusView {
@objc private func spoilerOverlayViewTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
delegate?.statusView(self, spoilerOverlayViewDidPressed: spoilerOverlayView)
}
@objc private func statusCardControlPressed(_ sender: StatusCardControl) {
guard let urlString = viewModel.card?.url, let url = URL(string: urlString) else { return }
delegate?.statusView(self, didTapCardWithURL: url)
}
}
extension StatusView {
@ -798,6 +792,10 @@ extension StatusView: MastodonMenuDelegate {
// MARK: StatusCardControlDelegate
extension StatusView: StatusCardControlDelegate {
public func statusCardControl(_ statusCardControl: StatusCardControl, didTapAuthor author: Mastodon.Entity.Account) {
delegate?.statusView(self, cardControl: statusCardControl, didTapProfile: author)
}
public func statusCardControl(_ statusCardControl: StatusCardControl, didTapURL url: URL) {
delegate?.statusView(self, cardControl: statusCardControl, didTapURL: url)
}