Compare commits

..

No commits in common. "a79b3cfd706c4065bb545dd134b2245e9e22c808" and "13d649bacec6258c44416d04bf2d03d234b56f02" have entirely different histories.

13 changed files with 57 additions and 151 deletions

View File

@ -257,8 +257,8 @@ private func setInstanceBreadcrumb(instance: Instance, nodeInfo: NodeInfo?) {
] ]
if let nodeInfo { if let nodeInfo {
crumb.data!["nodeInfo"] = [ crumb.data!["nodeInfo"] = [
"software": nodeInfo.software.name, "version": nodeInfo.version,
"version": nodeInfo.software.version, "software": nodeInfo.software,
] ]
} }
SentrySDK.addBreadcrumb(crumb: crumb) SentrySDK.addBreadcrumb(crumb: crumb)

View File

@ -52,11 +52,7 @@ class Preferences: Codable, ObservableObject {
self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger) self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger)
self.useTwitterKeyboard = try container.decodeIfPresent(Bool.self, forKey: .useTwitterKeyboard) ?? false self.useTwitterKeyboard = try container.decodeIfPresent(Bool.self, forKey: .useTwitterKeyboard) ?? false
if let blurAllMedia = try? container.decodeIfPresent(Bool.self, forKey: .blurAllMedia) { self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
self.attachmentBlurMode = blurAllMedia ? .always : .useStatusSetting
} else {
self.attachmentBlurMode = try container.decode(AttachmentBlurMode.self, forKey: .attachmentBlurMode)
}
self.blurMediaBehindContentWarning = try container.decodeIfPresent(Bool.self, forKey: .blurMediaBehindContentWarning) ?? true self.blurMediaBehindContentWarning = try container.decodeIfPresent(Bool.self, forKey: .blurMediaBehindContentWarning) ?? true
self.automaticallyPlayGifs = try container.decode(Bool.self, forKey: .automaticallyPlayGifs) self.automaticallyPlayGifs = try container.decode(Bool.self, forKey: .automaticallyPlayGifs)
@ -99,7 +95,7 @@ class Preferences: Codable, ObservableObject {
try container.encode(mentionReblogger, forKey: .mentionReblogger) try container.encode(mentionReblogger, forKey: .mentionReblogger)
try container.encode(useTwitterKeyboard, forKey: .useTwitterKeyboard) try container.encode(useTwitterKeyboard, forKey: .useTwitterKeyboard)
try container.encode(attachmentBlurMode, forKey: .attachmentBlurMode) try container.encode(blurAllMedia, forKey: .blurAllMedia)
try container.encode(blurMediaBehindContentWarning, forKey: .blurMediaBehindContentWarning) try container.encode(blurMediaBehindContentWarning, forKey: .blurMediaBehindContentWarning)
try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs) try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs)
@ -144,12 +140,10 @@ class Preferences: Codable, ObservableObject {
@Published var useTwitterKeyboard = false @Published var useTwitterKeyboard = false
// MARK: Media // MARK: Media
@Published var attachmentBlurMode = AttachmentBlurMode.useStatusSetting { @Published var blurAllMedia = false {
didSet { didSet {
if attachmentBlurMode == .always { if blurAllMedia {
blurMediaBehindContentWarning = true blurMediaBehindContentWarning = true
} else if attachmentBlurMode == .never {
blurMediaBehindContentWarning = false
} }
} }
} }
@ -197,8 +191,7 @@ class Preferences: Codable, ObservableObject {
case mentionReblogger case mentionReblogger
case useTwitterKeyboard case useTwitterKeyboard
case blurAllMedia // only used for migration case blurAllMedia
case attachmentBlurMode
case blurMediaBehindContentWarning case blurMediaBehindContentWarning
case automaticallyPlayGifs case automaticallyPlayGifs
@ -261,23 +254,4 @@ extension Preferences {
} }
} }
extension Preferences {
enum AttachmentBlurMode: Codable, Hashable, CaseIterable {
case useStatusSetting
case always
case never
var displayName: String {
switch self {
case .useStatusSetting:
return "Default"
case .always:
return "Always"
case .never:
return "Never"
}
}
}
}
extension UIUserInterfaceStyle: Codable {} extension UIUserInterfaceStyle: Codable {}

View File

@ -136,11 +136,6 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
vc.player?.play() vc.player?.play()
} }
} }
override func accessibilityPerformEscape() -> Bool {
dismiss(animated: true)
return true
}
// MARK: - Page View Controller Data Source // MARK: - Page View Controller Data Source

View File

@ -122,24 +122,5 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
displayNameLabel.updateForAccountDisplayName(account: account) displayNameLabel.updateForAccountDisplayName(account: account)
} }
} }
// MARK: Accessibility
override var isAccessibilityElement: Bool {
get { true }
set {}
}
override var accessibilityAttributedLabel: NSAttributedString? {
get {
guard let account else {
return nil
}
let s = NSMutableAttributedString(string: "\(account.displayName), ")
s.append(noteTextView.attributedText)
return s
}
set {}
}
} }

View File

@ -34,9 +34,8 @@ class ProfileDirectoryViewController: UIViewController {
title = NSLocalizedString("Profile Directory", comment: "profile directory title") title = NSLocalizedString("Profile Directory", comment: "profile directory title")
let filterItem = UIBarButtonItem(image: UIImage(systemName: "line.3.horizontal.decrease.circle"), menu: nil) // todo: it would be nice if there were a better "filter" icon
filterItem.accessibilityLabel = "Filter" navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "scope"), menu: nil)
navigationItem.rightBarButtonItem = filterItem
updateFilterMenu() updateFilterMenu()
let layout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, layoutEnvironment) in let layout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, layoutEnvironment) in

View File

@ -21,7 +21,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
@IBOutlet weak var descriptionLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel!
private var shareContainer: UIView! private var shareContainer: UIView!
private var closeContainer: UIView!
private var shareImage: UIImageView! private var shareImage: UIImageView!
private var shareButtonTopConstraint: NSLayoutConstraint! private var shareButtonTopConstraint: NSLayoutConstraint!
private var shareButtonLeadingConstraint: NSLayoutConstraint! private var shareButtonLeadingConstraint: NSLayoutConstraint!
@ -117,12 +116,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
view.addGestureRecognizer(doubleTap) view.addGestureRecognizer(doubleTap)
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
accessibilityElements = [
topControlsView!,
contentView,
bottomControlsView!,
]
} }
private func setupContentView() { private func setupContentView() {
@ -142,9 +135,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
private func setupControls() { private func setupControls() {
shareContainer = UIView() shareContainer = UIView()
shareContainer.isAccessibilityElement = true
shareContainer.accessibilityTraits = .button
shareContainer.accessibilityLabel = "Share"
shareContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(sharePressed))) shareContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(sharePressed)))
shareContainer.translatesAutoresizingMaskIntoConstraints = false shareContainer.translatesAutoresizingMaskIntoConstraints = false
topControlsView.addSubview(shareContainer) topControlsView.addSubview(shareContainer)
@ -171,10 +161,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
shareImage.heightAnchor.constraint(equalToConstant: 24), shareImage.heightAnchor.constraint(equalToConstant: 24),
]) ])
closeContainer = UIView() let closeContainer = UIView()
closeContainer.isAccessibilityElement = true
closeContainer.accessibilityTraits = .button
closeContainer.accessibilityLabel = "Close"
closeContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(closeButtonPressed))) closeContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(closeButtonPressed)))
closeContainer.translatesAutoresizingMaskIntoConstraints = false closeContainer.translatesAutoresizingMaskIntoConstraints = false
topControlsView.addSubview(closeContainer) topControlsView.addSubview(closeContainer)

View File

@ -21,18 +21,14 @@ struct MediaPrefsView: View {
var viewingSection: some View { var viewingSection: some View {
Section(header: Text("Viewing")) { Section(header: Text("Viewing")) {
Picker(selection: $preferences.attachmentBlurMode) { Toggle(isOn: $preferences.blurAllMedia) {
ForEach(Preferences.AttachmentBlurMode.allCases, id: \.self) { mode in Text("Blur All Media")
Text(mode.displayName).tag(mode)
}
} label: {
Text("Blur Media")
} }
Toggle(isOn: $preferences.blurMediaBehindContentWarning) { Toggle(isOn: $preferences.blurMediaBehindContentWarning) {
Text("Blur Media Behind Content Warning") Text("Blur Media Behind Content Warning")
} }
.disabled(preferences.attachmentBlurMode != .useStatusSetting) .disabled(preferences.blurAllMedia)
Toggle(isOn: $preferences.automaticallyPlayGifs) { Toggle(isOn: $preferences.automaticallyPlayGifs) {
Text("Automatically Play GIFs") Text("Automatically Play GIFs")

View File

@ -345,7 +345,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
private func removeTimelineDescriptionCell() { private func removeTimelineDescriptionCell() {
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
snapshot.deleteItems([.publicTimelineDescription]) snapshot.deleteSections([.header])
dataSource.apply(snapshot, animatingDifferences: true) dataSource.apply(snapshot, animatingDifferences: true)
isShowingTimelineDescription = false isShowingTimelineDescription = false
} }

View File

@ -173,17 +173,15 @@ class ContentTextView: LinkTextView, BaseEmojiLabel {
// MARK: - Navigation // MARK: - Navigation
func getViewController(forLink url: URL, inRange range: NSRange) -> UIViewController? { func getViewController(forLink url: URL, inRange range: NSRange) -> UIViewController {
let text = (self.text as NSString).substring(with: range) let text = (self.text as NSString).substring(with: range)
if let mention = getMention(for: url, text: text) { if let mention = getMention(for: url, text: text) {
return ProfileViewController(accountID: mention.id, mastodonController: mastodonController!) return ProfileViewController(accountID: mention.id, mastodonController: mastodonController!)
} else if let tag = getHashtag(for: url, text: text) { } else if let tag = getHashtag(for: url, text: text) {
return HashtagTimelineViewController(for: tag, mastodonController: mastodonController!) return HashtagTimelineViewController(for: tag, mastodonController: mastodonController!)
} else if url.scheme == "https" || url.scheme == "http" {
return SFSafariViewController(url: url)
} else { } else {
return nil return SFSafariViewController(url: url)
} }
} }

View File

@ -231,17 +231,16 @@ class BaseStatusTableViewCell: UITableViewCell {
func updateUIForPreferences(account: AccountMO, status: StatusMO) { func updateUIForPreferences(account: AccountMO, status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
switch Preferences.shared.attachmentBlurMode { if Preferences.shared.blurAllMedia {
case .never:
attachmentsView.contentHidden = false
case .always:
attachmentsView.contentHidden = true attachmentsView.contentHidden = true
default: } else if status.sensitive {
if status.sensitive { if !Preferences.shared.blurMediaBehindContentWarning && !status.spoilerText.isEmpty {
attachmentsView.contentHidden = status.spoilerText.isEmpty || Preferences.shared.blurMediaBehindContentWarning
} else {
attachmentsView.contentHidden = false attachmentsView.contentHidden = false
} else {
attachmentsView.contentHidden = true
} }
} else {
attachmentsView.contentHidden = false
} }
updateStatusIconsForPreferences(status) updateStatusIconsForPreferences(status)

View File

@ -148,17 +148,16 @@ extension StatusCollectionViewCell {
func baseUpdateUIForPreferences(status: StatusMO) { func baseUpdateUIForPreferences(status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.avatarImageViewSize avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.avatarImageViewSize
switch Preferences.shared.attachmentBlurMode { if Preferences.shared.blurAllMedia {
case .never:
contentContainer.attachmentsView.contentHidden = false
case .always:
contentContainer.attachmentsView.contentHidden = true contentContainer.attachmentsView.contentHidden = true
default: } else if status.sensitive {
if status.sensitive { if !Preferences.shared.blurMediaBehindContentWarning && !status.spoilerText.isEmpty {
contentContainer.attachmentsView.contentHidden = status.spoilerText.isEmpty || Preferences.shared.blurMediaBehindContentWarning
} else {
contentContainer.attachmentsView.contentHidden = false contentContainer.attachmentsView.contentHidden = false
} else {
contentContainer.attachmentsView.contentHidden = true
} }
} else {
contentContainer.attachmentsView.contentHidden = false
} }
let reblogButtonImage: UIImage let reblogButtonImage: UIImage

View File

@ -369,13 +369,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
guard let status = mastodonController.persistentContainer.status(for: statusID) else { guard let status = mastodonController.persistentContainer.status(for: statusID) else {
return nil return nil
} }
var str: AttributedString = "" var str = AttributedString("\(status.account.displayOrUserName), ")
if let rebloggerID,
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
str += AttributedString("Reblogged by \(reblogger.displayOrUserName): ")
}
str += AttributedString(status.account.displayOrUserName)
str += ", "
if statusState.collapsed ?? false { if statusState.collapsed ?? false {
if !status.spoilerText.isEmpty { if !status.spoilerText.isEmpty {
str += AttributedString(status.spoilerText) str += AttributedString(status.spoilerText)
@ -384,24 +378,15 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
str += "collapsed" str += "collapsed"
} else { } else {
str += AttributedString(contentTextView.attributedText) str += AttributedString(contentTextView.attributedText)
if status.attachments.count > 0 {
if status.attachments.count == 1 {
let attachment = status.attachments[0]
let desc = attachment.description?.isEmpty == false ? attachment.description! : "no description"
str += AttributedString(", attachment: \(desc)")
} else {
for (index, attachment) in status.attachments.enumerated() {
let desc = attachment.description?.isEmpty == false ? attachment.description! : "no description"
str += AttributedString(", attachment \(index + 1): \(desc)")
}
}
}
if status.poll != nil {
str += ", poll"
}
} }
if status.attachments.count > 0 {
// TODO: localize me
str += AttributedString(", \(status.attachments.count) attachment\(status.attachments.count > 1 ? "s" : "")")
}
if status.poll != nil {
str += ", poll"
}
str += AttributedString(", \(status.createdAt.formatted(.relative(presentation: .numeric)))") str += AttributedString(", \(status.createdAt.formatted(.relative(presentation: .numeric)))")
if status.visibility < .unlisted { if status.visibility < .unlisted {
str += AttributedString(", \(status.visibility.displayName)") str += AttributedString(", \(status.visibility.displayName)")
@ -409,6 +394,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
if status.localOnly { if status.localOnly {
str += ", local" str += ", local"
} }
if let rebloggerID,
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
str += AttributedString(", reblogged by \(reblogger.displayOrUserName)")
}
return NSAttributedString(str) return NSAttributedString(str)
} }
set {} set {}

View File

@ -254,13 +254,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
guard let status = mastodonController.persistentContainer.status(for: statusID) else { guard let status = mastodonController.persistentContainer.status(for: statusID) else {
return nil return nil
} }
var str: AttributedString = "" var str = AttributedString("\(status.account.displayOrUserName), ")
if let rebloggerID,
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
str += AttributedString("Reblogged by \(reblogger.displayOrUserName): ")
}
str += AttributedString(status.account.displayOrUserName)
str += ", "
if statusState.collapsed ?? false { if statusState.collapsed ?? false {
if !status.spoilerText.isEmpty { if !status.spoilerText.isEmpty {
str += AttributedString(status.spoilerText) str += AttributedString(status.spoilerText)
@ -269,24 +263,15 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
str += "collapsed" str += "collapsed"
} else { } else {
str += AttributedString(contentTextView.attributedText) str += AttributedString(contentTextView.attributedText)
if status.attachments.count > 0 {
if status.attachments.count == 1 {
let attachment = status.attachments[0]
let desc = attachment.description?.isEmpty == false ? attachment.description! : "no description"
str += AttributedString(", attachment: \(desc)")
} else {
for (index, attachment) in status.attachments.enumerated() {
let desc = attachment.description?.isEmpty == false ? attachment.description! : "no description"
str += AttributedString(", attachment \(index + 1): \(desc)")
}
}
}
if status.poll != nil {
str += ", poll"
}
} }
if status.attachments.count > 0 {
// TODO: localize me
str += AttributedString(", \(status.attachments.count) attachment\(status.attachments.count > 1 ? "s" : "")")
}
if status.poll != nil {
str += ", poll"
}
str += AttributedString(", \(status.createdAt.formatted(.relative(presentation: .numeric)))") str += AttributedString(", \(status.createdAt.formatted(.relative(presentation: .numeric)))")
if status.visibility < .unlisted { if status.visibility < .unlisted {
str += AttributedString(", \(status.visibility.displayName)") str += AttributedString(", \(status.visibility.displayName)")
@ -294,6 +279,10 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
if status.localOnly { if status.localOnly {
str += ", local" str += ", local"
} }
if let rebloggerID,
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
str += AttributedString(", reblogged by \(reblogger.displayOrUserName)")
}
return NSAttributedString(str) return NSAttributedString(str)
} }
set {} set {}