Compare commits

...

6 Commits

Author SHA1 Message Date
Shadowfacts b470ee6401 Fix status/mention push notifications not showing CW and fix sensitive attachments being included in push notifications
Closes #512
2024-07-21 18:13:48 -07:00
Shadowfacts fccd4e427c Fix profile moved header not being VO accessible
Closes #479
2024-07-21 14:03:18 -07:00
Shadowfacts f25031afd4 Fix profile moved view appearing behind avatar/header images 2024-07-21 13:46:07 -07:00
Shadowfacts ca65f84137 Lift pinned timelines modifiers up to CustomizeTimelinesList
.sheet on a Section with a header does not work, and produces warnings
about trying to present on a VC that already has a presentation. It
seems like the .sheet modifier is being applied to both the Section
content and header?

Fixes #514
2024-07-21 12:02:14 -07:00
Shadowfacts d4057adf4d Avoid updating AccountDisplayNameLabel when emojis pref hasn't changed
Oops
2024-07-21 10:36:24 -07:00
Shadowfacts 007937d2d7 Consolidate display/username labels on timeline statuses
Closes #513
2024-07-20 23:35:31 -07:00
12 changed files with 213 additions and 82 deletions

View File

@ -122,7 +122,12 @@ class NotificationService: UNNotificationServiceExtension {
let notificationContent: String? let notificationContent: String?
if let status = notification.status { if let status = notification.status {
if notification.kind == .mention || notification.kind == .status,
!status.spoilerText.isEmpty {
notificationContent = "⚠️ \(status.spoilerText)"
} else {
notificationContent = NotificationService.textConverter.convert(html: status.content) notificationContent = NotificationService.textConverter.convert(html: status.content)
}
} else if notification.kind == .follow || notification.kind == .followRequest { } else if notification.kind == .follow || notification.kind == .followRequest {
notificationContent = nil notificationContent = nil
} else { } else {
@ -135,7 +140,9 @@ class NotificationService: UNNotificationServiceExtension {
// We deliberately don't include attachments for other types of notifications that have statuses (favs, etc.) // We deliberately don't include attachments for other types of notifications that have statuses (favs, etc.)
// because we risk just fetching the same thing a bunch of times for many senders. // because we risk just fetching the same thing a bunch of times for many senders.
if notification.kind == .mention || notification.kind == .status || notification.kind == .update, if notification.kind == .mention || notification.kind == .status || notification.kind == .update,
let attachment = notification.status?.attachments.first { let status = notification.status,
!status.sensitive,
let attachment = status.attachments.first {
let url = attachment.previewURL ?? attachment.url let url = attachment.previewURL ?? attachment.url
attachmentDataTask = Task { attachmentDataTask = Task {
do { do {

View File

@ -235,6 +235,7 @@
D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46C2BD0B8310054DB14 /* AnnouncementsCollection.swift */; }; D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46C2BD0B8310054DB14 /* AnnouncementsCollection.swift */; };
D698F46F2BD0B8DF0054DB14 /* AddReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */; }; D698F46F2BD0B8DF0054DB14 /* AddReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */; };
D698F4712BD0CBAA0054DB14 /* AnnouncementContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D698F4702BD0CBAA0054DB14 /* AnnouncementContentTextView.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 */; }; D6A00B1D26379FC900316AD4 /* PollOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */; };
D6A3A380295515550036B6EF /* ProfileHeaderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */; }; D6A3A380295515550036B6EF /* ProfileHeaderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */; };
D6A3A3822956123A0036B6EF /* TimelinePosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3A3812956123A0036B6EF /* TimelinePosition.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 = "<group>"; }; D698F46C2BD0B8310054DB14 /* AnnouncementsCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementsCollection.swift; sourceTree = "<group>"; };
D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddReactionView.swift; sourceTree = "<group>"; }; D698F46E2BD0B8DF0054DB14 /* AddReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddReactionView.swift; sourceTree = "<group>"; };
D698F4702BD0CBAA0054DB14 /* AnnouncementContentTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnouncementContentTextView.swift; sourceTree = "<group>"; }; D698F4702BD0CBAA0054DB14 /* AnnouncementContentTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnouncementContentTextView.swift; sourceTree = "<group>"; };
D69F26332C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayAndUserNameLabel.swift; sourceTree = "<group>"; };
D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionsView.swift; sourceTree = "<group>"; }; D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionsView.swift; sourceTree = "<group>"; };
D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderButton.swift; sourceTree = "<group>"; }; D6A3A37F295515550036B6EF /* ProfileHeaderButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderButton.swift; sourceTree = "<group>"; };
D6A3A3812956123A0036B6EF /* TimelinePosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePosition.swift; sourceTree = "<group>"; }; D6A3A3812956123A0036B6EF /* TimelinePosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePosition.swift; sourceTree = "<group>"; };
@ -1480,7 +1482,9 @@
D6BED1722126661300F02DA0 /* Views */ = { D6BED1722126661300F02DA0 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */,
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameView.swift */, D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameView.swift */,
D69F26332C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift */,
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */, D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */,
D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */, D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */,
D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */, D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */,
@ -1514,7 +1518,6 @@
D641C78B213DD92F004B4513 /* Profile Header */, D641C78B213DD92F004B4513 /* Profile Header */,
D641C78A213DD926004B4513 /* Status */, D641C78A213DD926004B4513 /* Status */,
D64AAE8F26C80DB600FC57FB /* Toast */, D64AAE8F26C80DB600FC57FB /* Toast */,
D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2357,6 +2360,7 @@
D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */, D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */,
D62D67C52A97D8CD00167EE2 /* MultiColumnNavigationController.swift in Sources */, D62D67C52A97D8CD00167EE2 /* MultiColumnNavigationController.swift in Sources */,
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */, D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */,
D69F26342C4CDFD300FAF761 /* AccountDisplayAndUserNameLabel.swift in Sources */,
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */, D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */,
D68A76F129539116001DA1B3 /* FlipView.swift in Sources */, D68A76F129539116001DA1B3 /* FlipView.swift in Sources */,
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */, D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */,

View File

@ -13,7 +13,7 @@ struct CustomizeTimelinesView: View {
let mastodonController: MastodonController let mastodonController: MastodonController
var body: some View { var body: some View {
CustomizeTimelinesList() CustomizeTimelinesList(pinnedTimelines: mastodonController.accountPreferences.pinnedTimelines)
.environmentObject(mastodonController) .environmentObject(mastodonController)
.environment(\.managedObjectContext, mastodonController.persistentContainer.viewContext) .environment(\.managedObjectContext, mastodonController.persistentContainer.viewContext)
} }
@ -26,6 +26,14 @@ struct CustomizeTimelinesList: View {
@FetchRequest(sortDescriptors: []) private var filters: FetchedResults<FilterMO> @FetchRequest(sortDescriptors: []) private var filters: FetchedResults<FilterMO>
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var deletionError: (any Error)? @State private var deletionError: (any Error)?
// store this separately from AccountPreferences in the view, b/c the @LazilyDecoding wrapper breaks animations
@State private var pinnedTimelines: [PinnedTimeline]
@State private var isShowingAddHashtagSheet = false
@State private var isShowingAddInstanceSheet = false
init(pinnedTimelines: [PinnedTimeline]) {
self.pinnedTimelines = pinnedTimelines
}
var body: some View { var body: some View {
if #available(iOS 16.0, *) { if #available(iOS 16.0, *) {
@ -50,7 +58,11 @@ struct CustomizeTimelinesList: View {
private var navigationBody: some View { private var navigationBody: some View {
List { List {
PinnedTimelinesView(accountPreferences: mastodonController.accountPreferences) PinnedTimelinesView(
pinnedTimelines: $pinnedTimelines,
isShowingAddHashtagSheet: $isShowingAddHashtagSheet,
isShowingAddInstanceSheet: $isShowingAddInstanceSheet
)
.appGroupedListRowBackground() .appGroupedListRowBackground()
Section { Section {
@ -99,6 +111,12 @@ struct CustomizeTimelinesList: View {
} }
} }
} }
.modifier(PinnedTimelinesModifier(
accountPreferences: mastodonController.accountPreferences,
pinnedTimelines: $pinnedTimelines,
isShowingAddHashtagSheet: $isShowingAddHashtagSheet,
isShowingAddInstanceSheet: $isShowingAddInstanceSheet
))
.alertWithData("Error Deleting Filter", data: $deletionError, actions: { _ in .alertWithData("Error Deleting Filter", data: $deletionError, actions: { _ in
Button("OK") { Button("OK") {
self.deletionError = nil self.deletionError = nil

View File

@ -11,17 +11,10 @@ import Pachyderm
struct PinnedTimelinesView: View { struct PinnedTimelinesView: View {
@EnvironmentObject private var mastodonController: MastodonController @EnvironmentObject private var mastodonController: MastodonController
@ObservedObject private var accountPreferences: AccountPreferences
@State private var isShowingAddHashtagSheet = false @Binding var pinnedTimelines: [PinnedTimeline]
@State private var isShowingAddInstanceSheet = false @Binding var isShowingAddHashtagSheet: Bool
// store this separately from AccountPreferences in the view, b/c the @LazilyDecoding wrapper breaks animations @Binding var isShowingAddInstanceSheet: Bool
@State private var pinnedTimelines: [PinnedTimeline]
init(accountPreferences: AccountPreferences) {
self.accountPreferences = accountPreferences
self.pinnedTimelines = accountPreferences.pinnedTimelines
}
var body: some View { var body: some View {
Section { Section {
@ -110,6 +103,17 @@ struct PinnedTimelinesView: View {
} header: { } header: {
Text("Pinned Timelines") Text("Pinned Timelines")
} }
}
}
struct PinnedTimelinesModifier: ViewModifier {
let accountPreferences: AccountPreferences
@Binding var pinnedTimelines: [PinnedTimeline]
@Binding var isShowingAddHashtagSheet: Bool
@Binding var isShowingAddInstanceSheet: Bool
func body(content: Content) -> some View {
content
.sheet(isPresented: $isShowingAddHashtagSheet, content: { .sheet(isPresented: $isShowingAddHashtagSheet, content: {
#if os(visionOS) #if os(visionOS)
AddHashtagPinnedTimelineView(pinnedTimelines: $pinnedTimelines) AddHashtagPinnedTimelineView(pinnedTimelines: $pinnedTimelines)

View File

@ -22,7 +22,7 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var headerImageView: CachedImageView! @IBOutlet weak var headerImageView: CachedImageView!
@IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarContainerView: UIView!
@IBOutlet weak var avatarImageView: CachedImageView! @IBOutlet weak var avatarImageView: CachedImageView!
@IBOutlet weak var displayNameLabel: AccountDisplayNameLabel! @IBOutlet weak var displayAndUserNameLabel: AccountDisplayNameLabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var noteTextView: StatusContentTextView! @IBOutlet weak var noteTextView: StatusContentTextView!
@IBOutlet weak var suggestionSourceButton: UIButton! @IBOutlet weak var suggestionSourceButton: UIButton!
@ -49,8 +49,8 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell {
avatarImageView.layer.masksToBounds = true avatarImageView.layer.masksToBounds = true
avatarImageView.layer.cornerCurve = .continuous avatarImageView.layer.cornerCurve = .continuous
displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold)) displayAndUserNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold))
displayNameLabel.adjustsFontForContentSizeCategory = true displayAndUserNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 15, weight: .light)) usernameLabel.font = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 15, weight: .light))
usernameLabel.adjustsFontForContentSizeCategory = true usernameLabel.adjustsFontForContentSizeCategory = true
@ -96,7 +96,7 @@ class SuggestedProfileCardCollectionViewCell: UICollectionViewCell {
private func updateUIForPreferences(account: AccountMO) { private func updateUIForPreferences(account: AccountMO) {
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView) avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) 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 // Unneeded on visionOS since there is no light/dark mode

View File

@ -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
}
}

View File

@ -16,7 +16,7 @@ class AccountDisplayNameLabel: EmojiLabel {
private var accountDisplayName: String? private var accountDisplayName: String?
func updateForAccountDisplayName(account: some AccountProtocol) { func updateForAccountDisplayName(account: some AccountProtocol) {
guard accountID != account.id || accountDisplayName != account.displayName || Preferences.shared.hideCustomEmojiInUsernames == hasEmojis else { guard accountID != account.id || accountDisplayName != account.displayName || Preferences.shared.hideCustomEmojiInUsernames != hasEmojis else {
return return
} }
accountID = account.id accountID = account.id

View File

@ -14,6 +14,7 @@ class ProfileHeaderMovedOverlayView: UIView {
weak var delegate: TuskerNavigationDelegate? weak var delegate: TuskerNavigationDelegate?
var collapse: (() -> Void)? var collapse: (() -> Void)?
var hide: (() -> Void)?
private var avatarImageView: CachedImageView! private var avatarImageView: CachedImageView!
private var displayNameLabel: EmojiLabel! private var displayNameLabel: EmojiLabel!
@ -145,6 +146,45 @@ class ProfileHeaderMovedOverlayView: UIView {
delegate?.selected(account: movedToID) delegate?.selected(account: movedToID)
} }
// MARK: Accessibility
override var isAccessibilityElement: Bool {
get { true }
set {}
}
override var accessibilityLabel: String? {
get {
guard let movedToID,
let account = delegate?.apiController?.persistentContainer.account(for: movedToID) else {
return "This account has moved"
}
return "This account has moved to @\(account.acct)"
}
set {}
}
override func accessibilityActivate() -> Bool {
guard let movedToID,
let delegate else {
return false
}
delegate.selected(account: movedToID)
return true
}
override var accessibilityCustomActions: [UIAccessibilityCustomAction]? {
get {
[
UIAccessibilityCustomAction(name: "Hide banner", actionHandler: { [unowned self] _ in
self.hide?()
return true
})
]
}
set {}
}
} }
extension ProfileHeaderMovedOverlayView: UIPointerInteractionDelegate { extension ProfileHeaderMovedOverlayView: UIPointerInteractionDelegate {

View File

@ -41,6 +41,7 @@ class ProfileHeaderView: UIView {
@IBOutlet weak var followersCountButton: UIButton! @IBOutlet weak var followersCountButton: UIButton!
private(set) var pagesSegmentedControl: ScrollingSegmentedControl<ProfileViewController.Page>! private(set) var pagesSegmentedControl: ScrollingSegmentedControl<ProfileViewController.Page>!
private var movedOverlayView: ProfileHeaderMovedOverlayView? private var movedOverlayView: ProfileHeaderMovedOverlayView?
private var hideMovedOverlayView = false
var accountID: String! var accountID: String!
@ -178,7 +179,8 @@ class ProfileHeaderView: UIView {
followersCountButton.setAttributedTitle(followersCountTitle, for: .normal) followersCountButton.setAttributedTitle(followersCountTitle, for: .normal)
followersCountButton.accessibilityLabel = "\(followersSpelledOut) followers" followersCountButton.accessibilityLabel = "\(followersSpelledOut) followers"
if let movedTo = account.movedTo { if let movedTo = account.movedTo,
!hideMovedOverlayView {
if let movedOverlayView { if let movedOverlayView {
movedOverlayView.updateUI(movedTo: movedTo) movedOverlayView.updateUI(movedTo: movedTo)
} else { } else {
@ -207,6 +209,7 @@ class ProfileHeaderView: UIView {
private func createMovedOverlayView(movedTo: AccountMO) -> ProfileHeaderMovedOverlayView { private func createMovedOverlayView(movedTo: AccountMO) -> ProfileHeaderMovedOverlayView {
let overlay = ProfileHeaderMovedOverlayView() let overlay = ProfileHeaderMovedOverlayView()
overlay.layer.zPosition = 1000
overlay.delegate = delegate overlay.delegate = delegate
overlay.updateUI(movedTo: movedTo) overlay.updateUI(movedTo: movedTo)
overlay.translatesAutoresizingMaskIntoConstraints = false overlay.translatesAutoresizingMaskIntoConstraints = false
@ -234,6 +237,12 @@ class ProfileHeaderView: UIView {
} }
animator.startAnimation() animator.startAnimation()
} }
overlay.hide = { [weak self] in
guard let self else { return }
self.hideMovedOverlayView = true
self.updateUI(for: self.accountID)
UIAccessibility.post(notification: .layoutChanged, argument: self)
}
return overlay return overlay
} }

View File

@ -514,8 +514,15 @@ class ConversationMainStatusCollectionViewCell: UICollectionViewListCell, Status
return contentContainer.estimateVisibleSubviewHeight(effectiveWidth: width) return contentContainer.estimateVisibleSubviewHeight(effectiveWidth: width)
} }
func updateAccountUI(account: AccountMO) {
baseUpdateAccountUI(account: account)
displayNameLabel.updateForAccountDisplayName(account: account)
usernameLabel.text = "@\(account.acct)"
}
func updateUIForPreferences(status: StatusMO) { func updateUIForPreferences(status: StatusMO) {
baseUpdateUIForPreferences(status: status) baseUpdateUIForPreferences(status: status)
displayNameLabel.updateForAccountDisplayName(account: status.account)
} }
@objc private func preferencesChanged() { @objc private func preferencesChanged() {

View File

@ -21,8 +21,6 @@ protocol StatusCollectionViewCellDelegate: AnyObject, TuskerNavigationDelegate,
protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate { protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate {
// MARK: Subviews // MARK: Subviews
var avatarImageView: CachedImageView { get } var avatarImageView: CachedImageView { get }
var displayNameLabel: AccountDisplayNameLabel { get }
var usernameLabel: UILabel { get }
var contentWarningLabel: EmojiLabel { get } var contentWarningLabel: EmojiLabel { get }
var collapseButton: StatusCollapseButton { get } var collapseButton: StatusCollapseButton { get }
var contentContainer: StatusContentContainer { get } var contentContainer: StatusContentContainer { get }
@ -49,6 +47,7 @@ protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate
var isGrayscale: Bool { get set } var isGrayscale: Bool { get set }
var cancellables: Set<AnyCancellable> { get set } var cancellables: Set<AnyCancellable> { get set }
func updateAccountUI(account: AccountMO)
func updateAttachmentsUI(status: StatusMO) func updateAttachmentsUI(status: StatusMO)
func updateUIForPreferences(status: StatusMO) func updateUIForPreferences(status: StatusMO)
func updateStatusState(status: StatusMO) func updateStatusState(status: StatusMO)
@ -177,10 +176,8 @@ extension StatusCollectionViewCell {
attachmentsView.updateUI(attachments: status.attachments) attachmentsView.updateUI(attachments: status.attachments)
} }
func updateAccountUI(account: AccountMO) { func baseUpdateAccountUI(account: AccountMO) {
avatarImageView.update(for: account.avatar) avatarImageView.update(for: account.avatar)
displayNameLabel.updateForAccountDisplayName(account: account)
usernameLabel.text = "@\(account.acct)"
} }
func baseUpdateUIForPreferences(status: StatusMO) { func baseUpdateUIForPreferences(status: StatusMO) {
@ -219,7 +216,6 @@ extension StatusCollectionViewCell {
if contentTextView.hasEmojis { if contentTextView.hasEmojis {
contentTextView.setEmojis(status.emojis, identifier: status.id) contentTextView.setEmojis(status.emojis, identifier: status.id)
} }
displayNameLabel.updateForAccountDisplayName(account: status.account)
} }
func baseUpdateStatusState(status: StatusMO) { func baseUpdateStatusState(status: StatusMO) {

View File

@ -121,8 +121,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
} }
private lazy var nameHStack = UIStackView(arrangedSubviews: [ private lazy var nameHStack = UIStackView(arrangedSubviews: [
displayNameLabel, displayAndUserNameLabel,
usernameLabel,
pinImageView, pinImageView,
timestampLabel, timestampLabel,
]).configure { ]).configure {
@ -130,27 +129,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
$0.spacing = 4 $0.spacing = 4
} }
let displayNameLabel = AccountDisplayNameLabel().configure { let displayAndUserNameLabel = AccountDisplayAndUserNameLabel().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)
$0.adjustsFontForContentSizeCategory = true $0.adjustsFontForContentSizeCategory = true
$0.setContentHuggingPriority(.init(249), for: .horizontal) $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 { private let pinImageView = UIImageView(image: UIImage(systemName: "pin.fill")).configure {
@ -693,6 +675,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
.store(in: &cancellables) .store(in: &cancellables)
} }
func updateAccountUI(account: AccountMO) {
baseUpdateAccountUI(account: account)
displayAndUserNameLabel.updateUI(account: account)
}
func updateUIForPreferences(status: StatusMO) { func updateUIForPreferences(status: StatusMO) {
baseUpdateUIForPreferences(status: status) baseUpdateUIForPreferences(status: status)
@ -704,6 +691,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
metaIndicatorsView.updateUI(status: status) metaIndicatorsView.updateUI(status: status)
timelineReasonIcon.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.timelineReasonIconSize timelineReasonIcon.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.timelineReasonIconSize
displayAndUserNameLabel.updateUI(account: status.account)
} }
func updateStatusState(status: StatusMO) { func updateStatusState(status: StatusMO) {