diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index fd3b14e714..e57841e37e 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -235,6 +235,7 @@ D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46C2BD0B8310054DB14 /* AnnouncementsCollection.swift */; }; D698F46F2BD0B8DF0054DB14 /* AddReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */; }; D698F4712BD0CBAA0054DB14 /* AnnouncementContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F4702BD0CBAA0054DB14 /* AnnouncementContentTextView.swift */; }; + D69F26342C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F26332C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift */; }; D6A00B1D26379FC900316AD4 /* PollOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */; }; D6A3A380295515550036B6EF /* ProfileHeaderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */; }; D6A3A3822956123A0036B6EF /* TimelinePosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3A3812956123A0036B6EF /* TimelinePosition.swift */; }; @@ -668,6 +669,7 @@ D698F46C2BD0B8310054DB14 /* AnnouncementsCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementsCollection.swift; sourceTree = ""; }; D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddReactionView.swift; sourceTree = ""; }; D698F4702BD0CBAA0054DB14 /* AnnouncementContentTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnouncementContentTextView.swift; sourceTree = ""; }; + D69F26332C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayAndUserNameLabel.swift; sourceTree = ""; }; D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionsView.swift; sourceTree = ""; }; D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderButton.swift; sourceTree = ""; }; D6A3A3812956123A0036B6EF /* TimelinePosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePosition.swift; sourceTree = ""; }; @@ -1480,7 +1482,9 @@ D6BED1722126661300F02DA0 /* Views */ = { isa = PBXGroup; children = ( + D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */, D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameView.swift */, + D69F26332C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift */, D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */, D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */, D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */, @@ -1514,7 +1518,6 @@ D641C78B213DD92F004B4513 /* Profile Header */, D641C78A213DD926004B4513 /* Status */, D64AAE8F26C80DB600FC57FB /* Toast */, - D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */, ); path = Views; sourceTree = ""; @@ -2357,6 +2360,7 @@ D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */, D62D67C52A97D8CD00167EE2 /* MultiColumnNavigationController.swift in Sources */, D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */, + D69F26342C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift in Sources */, D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */, D68A76F129539116001DA1B3 /* FlipView.swift in Sources */, D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */, diff --git a/Tusker/Screens/Explore/SuggestedProfileCardCollectionViewCell.swift b/Tusker/Screens/Explore/SuggestedProfileCardCollectionViewCell.swift index 62879b03a0..ad97bca7fc 100644 --- a/Tusker/Screens/Explore/SuggestedProfileCardCollectionViewCell.swift +++ b/Tusker/Screens/Explore/SuggestedProfileCardCollectionViewCell.swift @@ -22,7 +22,7 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell { @IBOutlet weak var headerImageView: CachedImageView! @IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarImageView: CachedImageView! - @IBOutlet weak var displayNameLabel: AccountDisplayNameLabel! + @IBOutlet weak var displayAndUserNameLabel: AccountDisplayNameLabel! @IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var noteTextView: StatusContentTextView! @IBOutlet weak var suggestionSourceButton: UIButton! @@ -49,8 +49,8 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell { avatarImageView.layer.masksToBounds = true avatarImageView.layer.cornerCurve = .continuous - displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold)) - displayNameLabel.adjustsFontForContentSizeCategory = true + displayAndUserNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold)) + displayAndUserNameLabel.adjustsFontForContentSizeCategory = true usernameLabel.font = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 15, weight: .light)) usernameLabel.adjustsFontForContentSizeCategory = true @@ -96,7 +96,7 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell { private func updateUIForPreferences(account: AccountMO) { avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) - displayNameLabel.updateForAccountDisplayName(account: account) + displayAndUserNameLabel.updateForAccountDisplayName(account: account) } // Unneeded on visionOS since there is no light/dark mode diff --git a/Tusker/Views/AccountDisplayAndUserNameLabel.swift b/Tusker/Views/AccountDisplayAndUserNameLabel.swift new file mode 100644 index 0000000000..0e96350349 --- /dev/null +++ b/Tusker/Views/AccountDisplayAndUserNameLabel.swift @@ -0,0 +1,57 @@ +// +// AccountDisplayAndUserNameLabel.swift +// Tusker +// +// Created by Shadowfacts on 7/20/24. +// Copyright © 2024 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +class AccountDisplayAndUserNameLabel: EmojiLabel { + var baseFont: UIFontDescriptor = .preferredFontDescriptor(withTextStyle: .body) + + private var state: State? + + func updateUI(account: some AccountProtocol) { + let state = State(accountID: account.id, displayName: account.displayName, acct: account.acct) + guard state != self.state || Preferences.shared.hideCustomEmojiInUsernames != hasEmojis else { + return + } + self.state = state + self.attributedText = makeAttributedText(state: state) + if Preferences.shared.hideCustomEmojiInUsernames { + self.removeEmojis() + } else { + self.setEmojis(account.emojis, identifier: state.accountID) + } + } + + private func makeAttributedText(state: State) -> NSAttributedString { + let s = NSMutableAttributedString() + s.append(NSAttributedString(string: state.displayName, attributes: [ + .font: UIFont(descriptor: baseFont.addingAttributes([ + .traits: [ + UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue, + ] + ]), size: 0), + ])) + s.append(NSAttributedString(string: " ")) + s.append(NSAttributedString(string: "@\(state.acct)", attributes: [ + .font: UIFont(descriptor: baseFont.addingAttributes([ + .traits: [ + UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue, + ] + ]), size: 0), + .foregroundColor: UIColor.secondaryLabel, + ])) + return s + } + + private struct State: Equatable { + var accountID: String + var displayName: String + var acct: String + } +} diff --git a/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift b/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift index 549ff6d44a..8ed333be03 100644 --- a/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusCollectionViewCell.swift @@ -514,8 +514,15 @@ class ConversationMainStatusCollectionViewCell: UICollectionViewListCell, Status return contentContainer.estimateVisibleSubviewHeight(effectiveWidth: width) } + func updateAccountUI(account: AccountMO) { + baseUpdateAccountUI(account: account) + displayNameLabel.updateForAccountDisplayName(account: account) + usernameLabel.text = "@\(account.acct)" + } + func updateUIForPreferences(status: StatusMO) { baseUpdateUIForPreferences(status: status) + displayNameLabel.updateForAccountDisplayName(account: status.account) } @objc private func preferencesChanged() { diff --git a/Tusker/Views/Status/StatusCollectionViewCell.swift b/Tusker/Views/Status/StatusCollectionViewCell.swift index 2d85c3ecf3..287514beac 100644 --- a/Tusker/Views/Status/StatusCollectionViewCell.swift +++ b/Tusker/Views/Status/StatusCollectionViewCell.swift @@ -21,8 +21,6 @@ protocol StatusCollectionViewCellDelegate: AnyObject, TuskerNavigationDelegate, protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate { // MARK: Subviews var avatarImageView: CachedImageView { get } - var displayNameLabel: AccountDisplayNameLabel { get } - var usernameLabel: UILabel { get } var contentWarningLabel: EmojiLabel { get } var collapseButton: StatusCollapseButton { get } var contentContainer: StatusContentContainer { get } @@ -49,6 +47,7 @@ protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate var isGrayscale: Bool { get set } var cancellables: Set { get set } + func updateAccountUI(account: AccountMO) func updateAttachmentsUI(status: StatusMO) func updateUIForPreferences(status: StatusMO) func updateStatusState(status: StatusMO) @@ -177,10 +176,8 @@ extension StatusCollectionViewCell { attachmentsView.updateUI(attachments: status.attachments) } - func updateAccountUI(account: AccountMO) { + func baseUpdateAccountUI(account: AccountMO) { avatarImageView.update(for: account.avatar) - displayNameLabel.updateForAccountDisplayName(account: account) - usernameLabel.text = "@\(account.acct)" } func baseUpdateUIForPreferences(status: StatusMO) { @@ -219,7 +216,6 @@ extension StatusCollectionViewCell { if contentTextView.hasEmojis { contentTextView.setEmojis(status.emojis, identifier: status.id) } - displayNameLabel.updateForAccountDisplayName(account: status.account) } func baseUpdateStatusState(status: StatusMO) { diff --git a/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift b/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift index 0d00269c68..3d32d93165 100644 --- a/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift +++ b/Tusker/Views/Status/TimelineStatusCollectionViewCell.swift @@ -121,8 +121,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti } private lazy var nameHStack = UIStackView(arrangedSubviews: [ - displayNameLabel, - usernameLabel, + displayAndUserNameLabel, pinImageView, timestampLabel, ]).configure { @@ -130,27 +129,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti $0.spacing = 4 } - let displayNameLabel = AccountDisplayNameLabel().configure { - $0.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([ - .traits: [ - UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue, - ] - ]), size: 0) - $0.adjustsFontForContentSizeCategory = true - $0.setContentHuggingPriority(.init(251), for: .horizontal) - $0.setContentCompressionResistancePriority(.init(749), for: .horizontal) - } - - let usernameLabel = UILabel().configure { - $0.textColor = .secondaryLabel - $0.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([ - .traits: [ - UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue, - ] - ]), size: 0) + let displayAndUserNameLabel = AccountDisplayAndUserNameLabel().configure { $0.adjustsFontForContentSizeCategory = true $0.setContentHuggingPriority(.init(249), for: .horizontal) - $0.setContentCompressionResistancePriority(.init(748), for: .horizontal) + $0.setContentCompressionResistancePriority(.init(749), for: .horizontal) } private let pinImageView = UIImageView(image: UIImage(systemName: "pin.fill")).configure { @@ -693,6 +675,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti .store(in: &cancellables) } + func updateAccountUI(account: AccountMO) { + baseUpdateAccountUI(account: account) + displayAndUserNameLabel.updateUI(account: account) + } + func updateUIForPreferences(status: StatusMO) { baseUpdateUIForPreferences(status: status) @@ -704,6 +691,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti metaIndicatorsView.updateUI(status: status) timelineReasonIcon.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.timelineReasonIconSize + + displayAndUserNameLabel.updateUI(account: status.account) } func updateStatusState(status: StatusMO) {