Compare commits

...

20 Commits

Author SHA1 Message Date
Shadowfacts 68c3affacf Bump build number and update changelog 2022-11-05 18:31:22 -04:00
Shadowfacts e40f4faa8e Rewrite TrendingStatusesViewController to use collection view 2022-11-05 15:13:20 -04:00
Shadowfacts b56c6c37ec Fix crash when ProfileHeaderView tries to create observers after ProfileVC is deinit'd
Can happen if the network is slow and the user closes the profile screen before the header loads
2022-11-05 14:42:40 -04:00
Shadowfacts 999118798c Fix inserting pinned items that already exist when refreshing profile 2022-11-05 14:38:08 -04:00
Shadowfacts 84cf755332 Fix drawing VC background flickering in dark mode
Closes #199
2022-11-05 14:29:45 -04:00
Shadowfacts 5bd7c0ad2b Add preference to prevent blurring media behind CW
Closes #203
2022-11-05 13:20:55 -04:00
Shadowfacts 7fe06d42ce Consider content height, not just char count, when collapsing posts
Closes #205
2022-11-05 13:11:36 -04:00
Shadowfacts 20986ba3f0 Add preference for default reply visibility
Closes #207
2022-11-05 12:20:30 -04:00
Shadowfacts 97a95c435e Improve performance when displaying posts with many custom emojis
Closes #204
2022-11-05 11:00:14 -04:00
Shadowfacts b9555cf7dd Dynamic type support in assorted places 2022-11-04 22:32:40 -04:00
Shadowfacts 590b9f0bcc Dynamic type support on notifications screen 2022-11-04 22:32:34 -04:00
Shadowfacts ca2ceaea56 Remove now-unused confirm load more table view cell 2022-11-04 22:32:34 -04:00
Shadowfacts 96d8a79d42 Dynamic type support in Explore screen 2022-11-04 21:47:42 -04:00
Shadowfacts 11233f7d25 Dyanmic type support in profile header view 2022-11-04 21:39:47 -04:00
Shadowfacts a991e0f429 Dynamic Type support in status cells 2022-11-04 16:52:37 -04:00
Shadowfacts bfdce07d81 Fix compose reply avatar being wrongly aligned for 1-line statuses 2022-11-03 19:14:52 -04:00
Shadowfacts f5953655c5 Set merge policy on managed object contexts and maybe fix some CoreData errors? 2022-11-03 18:56:06 -04:00
Shadowfacts 6bc4993d81 Fix favorite/reblog menu actions not working 2022-11-03 18:48:39 -04:00
Shadowfacts 68646c4b4d Fix objc associated objects not working in release builds 2022-11-03 18:37:32 -04:00
Shadowfacts 38b0d57118 Improve CoreData error reporting 2022-11-03 10:27:45 -04:00
61 changed files with 991 additions and 477 deletions

View File

@ -1,5 +1,20 @@
# Changelog
## 2022.1 (44)
Features/Improvements:
- Dynamic Type support
- Improve performance when displaying statuses with large numbers of custom emojis
- Add preference for default reply visibility
- Add preference to turn off blurring media in posts with content warnings
Bugfixes:
- Fix drawing background flashing between black/white in dark mode
- Fix undo scroll-to-top not working in release builds
- Fix favorite and reblog menu actions not working
- Fix avatar in compose being wrongly aligned on short statuses
- Fix posts that are tall but have few characters not getting collapsed
- Fix crash when profile screen is closed for the profile loads
## 2022.1 (43)
Features/Improvements:
- Re-add undo scroll-to-top by tapping the status bar a second time

View File

@ -125,6 +125,7 @@
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */; };
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; };
@ -281,8 +282,6 @@
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A44273D6C5700386A6C /* GIFImageView.swift */; };
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; };
D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */; };
D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */; };
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */; };
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; };
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; };
@ -479,6 +478,7 @@
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldsView.swift; sourceTree = "<group>"; };
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = "<group>"; };
@ -643,8 +643,6 @@
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFImageView.swift; sourceTree = "<group>"; };
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; };
D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmLoadMoreTableViewCell.swift; sourceTree = "<group>"; };
D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfirmLoadMoreTableViewCell.xib; sourceTree = "<group>"; };
D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipMO.swift; sourceTree = "<group>"; };
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = "<group>"; };
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = "<group>"; };
@ -1061,6 +1059,7 @@
children = (
D6412B0A24B0D4C600F5412E /* ProfileHeaderView.xib */,
D6412B0C24B0D4CF00F5412E /* ProfileHeaderView.swift */,
D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */,
);
path = "Profile Header";
sourceTree = "<group>";
@ -1448,8 +1447,6 @@
D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */ = {
isa = PBXGroup;
children = (
D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */,
D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */,
D61A45E528DC0F2F002BE511 /* ConfirmLoadMoreCollectionViewCell.swift */,
);
path = "Confirm Load More Cell";
@ -1683,7 +1680,6 @@
D6F2E966249E8BFD005846BB /* IssueReporterViewController.xib in Resources */,
D662AEF0263A3B880082A153 /* PollFinishedTableViewCell.xib in Resources */,
D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */,
D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1774,7 +1770,6 @@
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */,
D6412B0324AFF6A600F5412E /* TabBarScrollableViewController.swift in Sources */,
D6093FB725BE0CF3004811E6 /* TrendHistoryView.swift in Sources */,
D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */,
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */,
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */,
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */,
@ -1816,6 +1811,7 @@
D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */,
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */,
D620483623D38075008A63EF /* ContentTextView.swift in Sources */,
D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */,
D6F2E965249E8BFD005846BB /* IssueReporterViewController.swift in Sources */,
D6A57408255C53EC00674551 /* ComposeTextViewCaretScrolling.swift in Sources */,
D6CA6A94249FADE700AD45C1 /* GalleryPlayerViewController.swift in Sources */,
@ -2170,7 +2166,7 @@
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2238,7 +2234,7 @@
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = OpenInTusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2388,7 +2384,7 @@
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2417,7 +2413,7 @@
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2527,7 +2523,7 @@
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = OpenInTusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -2554,7 +2550,7 @@
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 43;
CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = OpenInTusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;

View File

@ -26,6 +26,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.persistentStoreCoordinator = self.persistentStoreCoordinator
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context
}()
@ -33,6 +34,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.persistentStoreCoordinator = self.persistentStoreCoordinator
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context
}()
@ -62,6 +64,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
}
viewContext.automaticallyMergesChangesFromParent = true
viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext)
}
@ -72,12 +75,27 @@ class MastodonCachePersistentStore: NSPersistentContainer {
}
do {
try context.save()
} catch {
} catch let error as NSError {
logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)")
let crumb = Breadcrumb(level: .fatal, category: "PersistentStore")
crumb.message = String(describing: error)
// note: NSDetailedErrorsKey == "NSDetailedErrorsKey" != "NSDetailedErrors"
if let detailed = error.userInfo["NSDetailedErrors"] as? [NSError] {
crumb.data = [
"errors": detailed.compactMap { error -> [String: Any?]? in
guard let object = error.userInfo[NSValidationObjectErrorKey] as? NSManagedObject else {
return nil
}
return [
"entity": object.entity.name,
"key": error.userInfo[NSValidationKeyErrorKey],
"value": error.userInfo[NSValidationValueErrorKey],
"message": error.localizedDescription,
]
}
]
}
SentrySDK.addBreadcrumb(crumb: crumb)
fatalError("Unable to save managed object context")
fatalError("Unable to save managed object context: \(String(describing: error))")
}
}

View File

@ -22,8 +22,7 @@ extension NSTextAttachment {
image.draw(in: CGRect(origin: .zero, size: imageSizeMatchingFontSize))
}
self.init()
self.image = attachmentImage
self.init(image: attachmentImage)
}
convenience init(emojiPlaceholderIn font: UIFont) {
@ -31,7 +30,6 @@ extension NSTextAttachment {
// assumes emoji are mostly square
let size = CGSize(width: adjustedCapHeight, height: adjustedCapHeight)
let image = UIGraphicsImageRenderer(size: size).image { (_) in }
self.init()
self.image = image
self.init(image: image)
}
}

View File

@ -11,11 +11,10 @@ import Pachyderm
extension StatusState {
func resolveFor(status: StatusMO, text: String?) {
func resolveFor(status: StatusMO, height: CGFloat) {
let longEnoughToCollapse: Bool
if Preferences.shared.collapseLongPosts,
let text = text,
text.count > 500 {
height > 500 {
longEnoughToCollapse = true
} else {
longEnoughToCollapse = false

View File

@ -8,12 +8,12 @@
import UIKit
private var prevScrollOffsetBeforeScrollToTopKey: Void = ()
private let prevScrollOffsetBeforeScrollToTopKey = UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: 0)
extension UIScrollView {
private var prevScrollOffsetBeforeScrollToTop: CGFloat? {
get {
if let v = (objc_getAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey) as? NSNumber)?.doubleValue {
if let v = (objc_getAssociatedObject(self, prevScrollOffsetBeforeScrollToTopKey) as? NSNumber)?.doubleValue {
return CGFloat(v)
} else {
return nil
@ -21,9 +21,9 @@ extension UIScrollView {
}
set {
if let newValue {
objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_COPY_NONATOMIC)
objc_setAssociatedObject(self, prevScrollOffsetBeforeScrollToTopKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_COPY_NONATOMIC)
} else {
objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, nil, .OBJC_ASSOCIATION_COPY_NONATOMIC)
objc_setAssociatedObject(self, prevScrollOffsetBeforeScrollToTopKey, nil, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
}

View File

@ -64,3 +64,18 @@ extension Status.Visibility {
}
}
extension Status.Visibility: Comparable {
public static func < (lhs: Pachyderm.Status.Visibility, rhs: Pachyderm.Status.Visibility) -> Bool {
switch (lhs, rhs) {
case (.direct, .public), (.private, .public), (.unlisted, .public):
return true
case (.direct, .unlisted), (.private, .unlisted):
return true
case (.direct, .private):
return true
default:
return false
}
}
}

View File

@ -187,14 +187,14 @@ extension MastodonController {
func createDraft(inReplyToID: String? = nil, mentioningAcct: String? = nil) -> Draft {
var acctsToMention = [String]()
var visibility = Preferences.shared.defaultPostVisibility
var visibility = inReplyToID != nil ? Preferences.shared.defaultReplyVisibility.resolved : Preferences.shared.defaultPostVisibility
var contentWarning = ""
if let inReplyToID = inReplyToID,
let inReplyTo = persistentContainer.status(for: inReplyToID) {
acctsToMention.append(inReplyTo.account.acct)
acctsToMention.append(contentsOf: inReplyTo.mentions.map(\.acct))
visibility = inReplyTo.visibility
visibility = min(visibility, inReplyTo.visibility)
if !inReplyTo.spoilerText.isEmpty {
switch Preferences.shared.contentWarningCopyMode {

View File

@ -45,12 +45,14 @@ class Preferences: Codable, ObservableObject {
self.hideActionsInTimeline = try container.decodeIfPresent(Bool.self, forKey: .hideActionsInTimeline) ?? false
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility)
self.defaultReplyVisibility = try container.decodeIfPresent(ReplyVisibility.self, forKey: .defaultReplyVisibility) ?? .sameAsPost
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
self.requireAttachmentDescriptions = try container.decode(Bool.self, forKey: .requireAttachmentDescriptions)
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger)
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
self.blurMediaBehindContentWarning = try container.decodeIfPresent(Bool.self, forKey: .blurMediaBehindContentWarning) ?? true
self.automaticallyPlayGifs = try container.decode(Bool.self, forKey: .automaticallyPlayGifs)
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
@ -84,12 +86,14 @@ class Preferences: Codable, ObservableObject {
try container.encode(hideActionsInTimeline, forKey: .hideActionsInTimeline)
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
try container.encode(defaultReplyVisibility, forKey: .defaultReplyVisibility)
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
try container.encode(requireAttachmentDescriptions, forKey: .requireAttachmentDescriptions)
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
try container.encode(mentionReblogger, forKey: .mentionReblogger)
try container.encode(blurAllMedia, forKey: .blurAllMedia)
try container.encode(blurMediaBehindContentWarning, forKey: .blurMediaBehindContentWarning)
try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs)
try container.encode(openLinksInApps, forKey: .openLinksInApps)
@ -122,13 +126,21 @@ class Preferences: Codable, ObservableObject {
// MARK: Composing
@Published var defaultPostVisibility = Status.Visibility.public
@Published var defaultReplyVisibility = ReplyVisibility.sameAsPost
@Published var automaticallySaveDrafts = true
@Published var requireAttachmentDescriptions = false
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
@Published var mentionReblogger = false
// MARK: Media
@Published var blurAllMedia = false
@Published var blurAllMedia = false {
didSet {
if blurAllMedia {
blurMediaBehindContentWarning = true
}
}
}
@Published var blurMediaBehindContentWarning = true
@Published var automaticallyPlayGifs = true
// MARK: Behavior
@ -164,12 +176,14 @@ class Preferences: Codable, ObservableObject {
case hideActionsInTimeline
case defaultPostVisibility
case defaultReplyVisibility
case automaticallySaveDrafts
case requireAttachmentDescriptions
case contentWarningCopyMode
case mentionReblogger
case blurAllMedia
case blurMediaBehindContentWarning
case automaticallyPlayGifs
case openLinksInApps
@ -194,4 +208,40 @@ class Preferences: Codable, ObservableObject {
}
extension Preferences {
enum ReplyVisibility: Codable, Hashable, CaseIterable {
case sameAsPost
case visibility(Status.Visibility)
static var allCases: [Preferences.ReplyVisibility] = [.sameAsPost] + Status.Visibility.allCases.map { .visibility($0) }
var resolved: Status.Visibility {
switch self {
case .sameAsPost:
return Preferences.shared.defaultPostVisibility
case .visibility(let vis):
return vis
}
}
var displayName: String {
switch self {
case .sameAsPost:
return "Same as Default"
case .visibility(let vis):
return vis.displayName
}
}
var imageName: String? {
switch self {
case .sameAsPost:
return nil
case .visibility(let vis):
return vis.imageName
}
}
}
}
extension UIUserInterfaceStyle: Codable {}

View File

@ -49,7 +49,6 @@ struct ComposeAttachmentRow: View {
ComposeTextView(text: $attachment.attachmentDescription, placeholder: Text("Describe for the visually impaired…"), minHeight: 80)
.heightDidChange(self.heightChanged)
.backgroundColor(.clear)
.fontSize(17)
case .recognizingText:
ProgressView()

View File

@ -66,11 +66,11 @@ struct ComposeAutocompleteMentionsView: View {
.cornerRadius(preferences.avatarStyle.cornerRadiusFraction * 30)
VStack(alignment: .leading) {
AccountDisplayNameLabel(account: account.value, fontSize: 14)
AccountDisplayNameLabel(account: account.value, textStyle: .subheadline, emojiSize: 14)
.foregroundColor(Color(UIColor.label))
Text(verbatim: "@\(account.value.acct)")
.font(.system(size: 12))
.font(.caption)
.foregroundColor(Color(UIColor.label))
}
}
@ -174,6 +174,7 @@ struct ComposeAutocompleteEmojisView: View {
@State var expanded = false
@State private var emojis: [Emoji] = []
@ScaledMetric private var emojiSize = 30
var body: some View {
// When exapnded, the toggle button should be at the top. When collapsed, it should be centered.
@ -212,13 +213,13 @@ struct ComposeAutocompleteEmojisView: View {
private var verticalGrid: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 30), spacing: 4)]) {
LazyVGrid(columns: [GridItem(.adaptive(minimum: emojiSize), spacing: 4)]) {
ForEach(emojis, id: \.shortcode) { (emoji) in
Button {
uiState.currentInput?.autocomplete(with: ":\(emoji.shortcode):")
} label: {
CustomEmojiImageView(emoji: emoji)
.frame(height: 30)
.frame(height: emojiSize)
}
}
}
@ -236,19 +237,19 @@ struct ComposeAutocompleteEmojisView: View {
} label: {
HStack(spacing: 4) {
CustomEmojiImageView(emoji: emoji)
.frame(height: 30)
.frame(height: emojiSize)
Text(verbatim: ":\(emoji.shortcode):")
.foregroundColor(Color(UIColor.label))
}
}
.frame(height: 30)
.frame(height: emojiSize)
}
.animation(.linear(duration: 0.2), value: emojis)
Spacer(minLength: 30)
Spacer(minLength: emojiSize)
}
.padding(.horizontal, 8)
.frame(height: 46)
.frame(height: emojiSize + 16)
}
}

View File

@ -27,11 +27,11 @@ struct ComposeCurrentAccount: View {
if let id = account?.id,
let account = mastodonController.persistentContainer.account(for: id) {
VStack(alignment: .leading) {
AccountDisplayNameLabel(account: account, fontSize: 20)
AccountDisplayNameLabel(account: account, textStyle: .title2, emojiSize: 24)
.lineLimit(1)
Text(verbatim: "@\(account.acct)")
.font(.system(size: 17, weight: .light))
.font(.body.weight(.light))
.foregroundColor(.secondary)
.lineLimit(1)
}

View File

@ -14,6 +14,30 @@ protocol ComposeDrawingViewControllerDelegate: AnyObject {
func composeDrawingViewController(_ drawingController: ComposeDrawingViewController, saveDrawing drawing: PKDrawing)
}
class ComposeDrawingNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.darkContent
}
init(editing initialDrawing: PKDrawing, delegate: ComposeDrawingViewControllerDelegate) {
let vc = ComposeDrawingViewController(editing: initialDrawing)
vc.delegate = delegate
super.init(rootViewController: vc)
modalPresentationStyle = .fullScreen
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
overrideUserInterfaceStyle = .light
}
}
class ComposeDrawingViewController: UIViewController {
weak var delegate: ComposeDrawingViewControllerDelegate?
@ -63,7 +87,8 @@ class ComposeDrawingViewController: UIViewController {
canvasView.drawingPolicy = .anyInput
canvasView.minimumZoomScale = 0.5
canvasView.maximumZoomScale = 2
canvasView.backgroundColor = .systemBackground
canvasView.backgroundColor = .white
canvasView.overrideUserInterfaceStyle = .light
canvasView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(canvasView)
NSLayoutConstraint.activate([
@ -74,6 +99,7 @@ class ComposeDrawingViewController: UIViewController {
])
toolPicker = PKToolPicker()
toolPicker.overrideUserInterfaceStyle = .light
toolPicker.setVisible(true, forFirstResponder: canvasView)
toolPicker.addObserver(canvasView)
toolPicker.addObserver(self)

View File

@ -46,12 +46,13 @@ struct ComposeEmojiTextField: UIViewRepresentable {
view.placeholder = placeholder
view.borderStyle = .roundedRect
view.font = .preferredFont(forTextStyle: .body)
view.adjustsFontForContentSizeCategory = true
view.backgroundColor = backgroundColor
view.delegate = context.coordinator
view.addTarget(context.coordinator, action: #selector(Coordinator.didChange(_:)), for: .editingChanged)
view.backgroundColor = backgroundColor
// otherwise when the text gets too wide it starts expanding the ComposeView
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

View File

@ -360,11 +360,7 @@ extension ComposeHostingController: ComposeUIStateDelegate {
drawing = PKDrawing()
}
let drawingVC = ComposeDrawingViewController(editing: drawing)
drawingVC.delegate = self
let nav = UINavigationController(rootViewController: drawingVC)
nav.modalPresentationStyle = .fullScreen
present(nav, animated: true)
present(ComposeDrawingNavigationController(editing: drawing, delegate: self), animated: true)
}
}

View File

@ -26,7 +26,7 @@ struct ComposeReplyView: View {
VStack(alignment: .leading, spacing: 0) {
HStack {
AccountDisplayNameLabel(account: status.account, fontSize: 17)
AccountDisplayNameLabel(account: status.account, textStyle: .body, emojiSize: 17)
.lineLimit(1)
.layoutPriority(1)
@ -51,6 +51,7 @@ struct ComposeReplyView: View {
.frame(height: contentHeight ?? 0)
}
}
.frame(minHeight: 50, alignment: .top)
}
private func replyAvatarImage(geometry: GeometryProxy) -> some View {
@ -63,7 +64,7 @@ struct ComposeReplyView: View {
offset = max(offset, 0)
// subtract 50, because we care about where the bottom of the view is but the offset is relative to the top of the view
let maxOffset = (contentHeight ?? 0) + (displayNameHeight ?? 0) - 50
let maxOffset = max((contentHeight ?? 0) + (displayNameHeight ?? 0) - 50, 0)
// once you scroll past the in-reply-to-content, the bottom of the avatar should be pinned to the bottom of the content
offset = min(offset, maxOffset)

View File

@ -15,7 +15,6 @@ struct ComposeTextView: View {
private var heightDidChange: ((CGFloat) -> Void)?
private var backgroundColor = UIColor.secondarySystemBackground
private var fontSize: CGFloat = 20
@State private var height: CGFloat?
@ -31,7 +30,7 @@ struct ComposeTextView: View {
if text.isEmpty, let placeholder = placeholder {
placeholder
.font(.system(size: fontSize))
.font(.body)
.foregroundColor(.secondary)
.offset(x: 4, y: 8)
}
@ -39,7 +38,7 @@ struct ComposeTextView: View {
WrappedTextView(
text: $text,
textDidChange: self.textDidChange,
font: .systemFont(ofSize: fontSize)
font: .preferredFont(forTextStyle: .body)
)
.frame(height: height ?? minHeight)
}
@ -61,12 +60,6 @@ struct ComposeTextView: View {
copy.backgroundColor = color
return copy
}
func fontSize(_ size: CGFloat) -> Self {
var copy = self
copy.fontSize = size
return copy
}
}
struct WrappedTextView: UIViewRepresentable {
@ -82,6 +75,7 @@ struct WrappedTextView: UIViewRepresentable {
textView.isEditable = true
textView.backgroundColor = .clear
textView.font = font
textView.adjustsFontForContentSizeCategory = true
textView.textContainer.lineBreakMode = .byWordWrapping
return textView
}

View File

@ -33,6 +33,7 @@ struct MainComposeTextView: View {
@State private var height: CGFloat?
@State private var becomeFirstResponder: Bool = false
@State private var hasFirstAppeared = false
@ScaledMetric private var fontSize = 20
var body: some View {
ZStack(alignment: .topLeading) {
@ -40,7 +41,7 @@ struct MainComposeTextView: View {
if draft.text.isEmpty {
placeholder
.font(.system(size: 20))
.font(.system(size: fontSize))
.foregroundColor(.secondary)
.offset(x: 4, y: 8)
}
@ -79,7 +80,8 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
textView.delegate = context.coordinator
textView.isEditable = true
textView.backgroundColor = .clear
textView.font = .systemFont(ofSize: 20)
textView.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 20))
textView.adjustsFontForContentSizeCategory = true
textView.textContainer.lineBreakMode = .byWordWrapping
context.coordinator.textView = textView
return textView

View File

@ -29,7 +29,11 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
noteTextView.defaultFont = .systemFont(ofSize: 16)
displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold))
displayNameLabel.adjustsFontForContentSizeCategory = true
noteTextView.defaultFont = .preferredFont(forTextStyle: .body)
noteTextView.adjustsFontForContentSizeCategory = true
noteTextView.textContainer.lineBreakMode = .byTruncatingTail
noteTextView.textContainerInset = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4)

View File

@ -9,76 +9,162 @@
import UIKit
import Pachyderm
class TrendingStatusesViewController: EnhancedTableViewController {
class TrendingStatusesViewController: UIViewController {
weak var mastodonController: MastodonController!
private var dataSource: UITableViewDiffableDataSource<Section, Item>!
private var collectionView: UICollectionView {
view as! UICollectionView
}
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
super.init(style: .grouped)
super.init(nibName: nil, bundle: nil)
dragEnabled = true
title = NSLocalizedString("Trending Posts", comment: "trending posts screen title")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
override func loadView() {
var config = UICollectionLayoutListConfiguration(appearance: .plain)
config.leadingSwipeActionsConfigurationProvider = { [unowned self] in
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.leadingSwipeActions()
}
config.trailingSwipeActionsConfigurationProvider = { [unowned self] in
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.trailingSwipeActions()
}
config.itemSeparatorHandler = { [unowned self] indexPath, sectionSeparatorConfiguration in
guard let item = self.dataSource.itemIdentifier(for: indexPath) else {
return sectionSeparatorConfiguration
}
var config = sectionSeparatorConfiguration
if item.hideSeparators {
config.topSeparatorVisibility = .hidden
config.bottomSeparatorVisibility = .hidden
}
if case .status(_, _) = item {
config.topSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
}
return config
}
let layout = UICollectionViewCompositionalLayout.list(using: config)
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dragDelegate = self
title = NSLocalizedString("Trending Posts", comment: "trending posts screen title")
dataSource = createDataSource()
}
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell")
tableView.estimatedRowHeight = 144
dataSource = UITableViewDiffableDataSource(tableView: tableView, cellProvider: { tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in
cell.delegate = self
cell.updateUI(statusID: item.id, state: item.state)
return cell
})
cell.updateUI(statusID: item.0, state: item.1)
}
let loadingCell = UICollectionView.CellRegistration<LoadingCollectionViewCell, Void> { cell, _, _ in
cell.indicator.startAnimating()
}
return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
switch itemIdentifier {
case .status(id: let id, state: let state):
return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state))
case .loadingIndicator:
return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ())
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let request = Client.getTrendingStatuses()
Task {
guard let (statuses, _) = try? await mastodonController.run(request) else {
return
}
mastodonController.persistentContainer.addAll(statuses: statuses) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses])
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) })
self.dataSource.apply(snapshot)
}
snapshot.appendItems([.loadingIndicator])
dataSource.apply(snapshot, animatingDifferences: false)
Task {
await loadTrendingStatuses()
}
}
// MARK: - Table View Delegate
private func loadTrendingStatuses() async {
let statuses: [Status]
do {
statuses = try await mastodonController.run(Client.getTrendingStatuses()).0
} catch {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
await dataSource.apply(snapshot)
let config = ToastConfiguration(from: error, with: "Loading Trending Posts", in: self) { toast in
toast.dismissToast(animated: true)
await self.loadTrendingStatuses()
}
showToast(configuration: config, animated: true)
return
}
await mastodonController.persistentContainer.addAll(statuses: statuses)
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses])
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) })
await dataSource.apply(snapshot)
}
}
extension TrendingStatusesViewController {
enum Section {
case statuses
}
struct Item: Hashable {
let id: String
let state: StatusState
enum Item: Hashable {
case status(id: String, state: StatusState)
case loadingIndicator
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.id == rhs.id
var hideSeparators: Bool {
if case .loadingIndicator = self {
return true
} else {
return false
}
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
var isSelectable: Bool {
if case .status(id: _, state: _) = self {
return true
} else {
return false
}
}
}
}
extension TrendingStatusesViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return dataSource.itemIdentifier(for: indexPath)?.isSelectable ?? false
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard case .status(id: let id, state: let state) = dataSource.itemIdentifier(for: indexPath) else {
return
}
selected(status: id, state: state.copy())
}
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return (collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.contextMenuConfiguration()
}
func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
MenuPreviewHelper.willPerformPreviewAction(animator: animator, presenter: self)
}
}
extension TrendingStatusesViewController: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
(collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.dragItemsForBeginning(session: session) ?? []
}
}
extension TrendingStatusesViewController: TuskerNavigationDelegate {
@ -91,9 +177,19 @@ extension TrendingStatusesViewController: ToastableViewController {
extension TrendingStatusesViewController: MenuActionProvider {
}
extension TrendingStatusesViewController: StatusTableViewCellDelegate {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates()
tableView.endUpdates()
extension TrendingStatusesViewController: StatusCollectionViewCellDelegate {
func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) {
if let indexPath = collectionView.indexPath(for: cell) {
var snapshot = dataSource.snapshot()
snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
dataSource.apply(snapshot, animatingDifferences: animated, completion: completion)
}
}
}
extension TrendingStatusesViewController: StatusBarTappableViewController {
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
collectionView.scrollToTop()
return .stop
}
}

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -42,6 +42,7 @@
<constraint firstAttribute="height" constant="20" id="4tF-oL-qXT"/>
<constraint firstAttribute="width" constant="20" id="zWx-jJ-dBj"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" image="square.and.arrow.up" catalog="system"/>
<connections>
@ -54,7 +55,7 @@
<constraint firstAttribute="width" constant="20" id="eg0-hN-rda"/>
<constraint firstAttribute="height" constant="20" id="fmA-pI-8WB"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" image="xmark" catalog="system"/>
<connections>
@ -75,9 +76,9 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPa-Zu-T6g">
<rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eo5-fc-RV8">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eo5-fc-RV8">
<rect key="frame" x="16" y="8" width="343" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>

View File

@ -14,6 +14,7 @@ struct ComposingPrefsView: View {
var body: some View {
List {
visibilitySection
composingSection
replyingSection
}
@ -21,9 +22,9 @@ struct ComposingPrefsView: View {
.navigationBarTitle("Composing")
}
var composingSection: some View {
Section(header: Text("Composing")) {
Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Post Visibility")) {
var visibilitySection: some View {
Section {
Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Visibility")) {
ForEach(Status.Visibility.allCases, id: \.self) { visibility in
HStack {
Image(systemName: visibility.imageName)
@ -33,6 +34,26 @@ struct ComposingPrefsView: View {
}//.navigationBarTitle("Default Post Visibility")
// navbar title on the ForEach is currently incorrectly applied when the picker is not expanded, see FB6838291
}
Picker(selection: $preferences.defaultReplyVisibility, label: Text("Reply Visibility")) {
ForEach(Preferences.ReplyVisibility.allCases, id: \.self) { visibility in
HStack {
if let imageName = visibility.imageName {
Image(systemName: imageName)
}
Text(visibility.displayName)
}
.tag(visibility)
}
}
} header: {
Text("Visibility")
} footer: {
Text("When starting a reply, Tusker will use your preferred visibility or the visibility of the post to which you're replying, whichever is narrower.")
}
}
var composingSection: some View {
Section(header: Text("Composing")) {
Toggle(isOn: $preferences.automaticallySaveDrafts) {
Text("Automatically Save Drafts")
}

View File

@ -24,6 +24,12 @@ struct MediaPrefsView: View {
Toggle(isOn: $preferences.blurAllMedia) {
Text("Blur All Media")
}
Toggle(isOn: $preferences.blurMediaBehindContentWarning) {
Text("Blur Media Behind Content Warning")
}
.disabled(preferences.blurAllMedia)
Toggle(isOn: $preferences.automaticallyPlayGifs) {
Text("Automatically Play GIFs")
}

View File

@ -222,7 +222,17 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
}
var snapshot = dataSource.snapshot()
let items = statuses.map { Item.status(id: $0.id, state: .unknown, pinned: true) }
let existingPinned = snapshot.itemIdentifiers(inSection: .pinned)
let items = statuses.map {
let item = Item.status(id: $0.id, state: .unknown, pinned: true)
// try to keep the existing status state
if let existing = existingPinned.first(where: { $0 == item }) {
return existing
} else {
return item
}
}
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .pinned))
snapshot.appendItems(items, toSection: .pinned)
await apply(snapshot, animatingDifferences: true)
}

View File

@ -185,21 +185,11 @@ extension MenuActionProvider {
favImage = favImage.withTintColor(UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1), renderingMode: .alwaysOriginal)
}
toggleableSection.insert(createAction(identifier: "favorite", title: favorited ? "Unfavorite" : "Favorite", image: favImage, handler: { [weak self] _ in
guard let self = self else { return }
let request = (favorited ? Status.favourite : Status.unfavourite)(status.id)
self.mastodonController?.run(request, completion: { response in
switch response {
case .success(let status, _):
self.mastodonController?.persistentContainer.addOrUpdate(status: status)
case .failure(let error):
if let toastable = self.toastableViewController {
let config = ToastConfiguration(from: error, with: "Error \(favorited ? "Unf" : "F")avoriting", in: toastable, retryAction: nil)
DispatchQueue.main.async {
toastable.showToast(configuration: config, animated: true)
guard let self,
let navigationDelegate = self.navigationDelegate else { return }
Task { @MainActor in
await FavoriteService(status: status, mastodonController: mastodonController, presenter: navigationDelegate).toggleFavorite()
}
}
}
})
}), at: 0)
let reblogged = status.reblogged
@ -208,26 +198,11 @@ extension MenuActionProvider {
reblogImage = reblogImage.withTintColor(UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1), renderingMode: .alwaysOriginal)
}
toggleableSection.insert(createAction(identifier: "reblog", title: reblogged ? "Unreblog" : "Reblog", image: reblogImage, handler: { [weak self] _ in
guard let self = self else { return }
let request: Request<Status>
if reblogged {
request = Status.reblog(status.id)
} else {
request = Status.unreblog(status.id)
guard let self,
let navigationDelegate = self.navigationDelegate else { return }
Task { @MainActor in
await ReblogService(status: status, mastodonController: mastodonController, presenter: navigationDelegate).toggleReblog()
}
self.mastodonController?.run(request, completion: { response in
switch response {
case .success(let status, _):
self.mastodonController?.persistentContainer.addOrUpdate(status: status)
case .failure(let error):
if let toastable = self.toastableViewController {
let config = ToastConfiguration(from: error, with: "Error \(reblogged ? "Unr" : "R")eblogging", in: toastable, retryAction: nil)
DispatchQueue.main.async {
toastable.showToast(configuration: config, animated: true)
}
}
}
})
}), at: 1)
}
@ -362,7 +337,7 @@ extension MenuActionProvider {
]
}
private func createAction(identifier: String, title: String, systemImageName: String?, handler: @escaping UIActionHandler) -> UIAction {
private func createAction(identifier: String, title: String, systemImageName: String?, handler: @escaping (UIAction) -> Void) -> UIAction {
let image: UIImage?
if let name = systemImageName {
image = UIImage(systemName: name)

View File

@ -29,6 +29,12 @@ class AccountTableViewCell: UITableViewCell {
avatarImageView.layer.masksToBounds = true
usernameLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .light))
usernameLabel.adjustsFontForContentSizeCategory = true
noteLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15))
noteLabel.adjustsFontForContentSizeCategory = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPrefrences), name: .preferencesChanged, object: nil)
}

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -26,16 +28,16 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5">
<rect key="frame" x="74" y="11" width="230" height="78"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="230" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JMo-QH-1is">
<rect key="frame" x="0.0" y="20.5" width="230" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Note" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bNO-qR-YEe" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
@ -66,4 +68,9 @@
<point key="canvasLocation" x="173.91304347826087" y="35.491071428571423"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@ -14,20 +14,22 @@ private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options:
struct AccountDisplayNameLabel: View {
let account: any AccountProtocol
let fontSize: Int
let textStyle: Font.TextStyle
@ScaledMetric var emojiSize: CGFloat
@State var text: Text
@State var emojiRequests = [ImageCache.Request]()
init(account: any AccountProtocol, fontSize: Int) {
init(account: any AccountProtocol, textStyle: Font.TextStyle, emojiSize: CGFloat) {
self.account = account
self.fontSize = fontSize
self.textStyle = textStyle
self._emojiSize = ScaledMetric(wrappedValue: emojiSize, relativeTo: textStyle)
let name = account.displayName.isEmpty ? account.username : account.displayName
self._text = State(initialValue: Text(verbatim: name))
}
var body: some View {
text
.font(.system(size: CGFloat(fontSize), weight: .semibold))
.font(.system(textStyle).weight(.semibold))
.onAppear(perform: self.loadEmojis)
}
@ -53,7 +55,7 @@ struct AccountDisplayNameLabel: View {
defer { group.leave() }
guard let image = image else { return }
let size = CGSize(width: fontSize, height: fontSize)
let size = CGSize(width: emojiSize, height: emojiSize)
let renderer = UIGraphicsImageRenderer(size: size)
let resized = renderer.image { (ctx) in
image.draw(in: CGRect(origin: .zero, size: size))

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -23,9 +24,9 @@
<constraint firstAttribute="height" constant="64" id="ZhL-aE-TCF"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Title" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fK1-aD-yvs">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Title" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fK1-aD-yvs">
<rect key="frame" x="96" y="30" width="216" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -23,9 +24,9 @@
<constraint firstAttribute="width" constant="64" id="P7K-i4-Id7"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="All Photos" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pcI-Ow-ilI">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="All Photos" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pcI-Ow-ilI">
<rect key="frame" x="96" y="30" width="216" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>

View File

@ -236,27 +236,9 @@ class AttachmentsContainerView: UIView {
// Make sure accessibilityElements is set every time the UI is updated, otherwise it holds
// on to strong references to the old set of attachment views
self.accessibilityElements = accessibilityElements
contentHidden = Preferences.shared.blurAllMedia || status.sensitive
}
private func createAttachmentView(index: Int, hSize: RelativeSize, vSize: RelativeSize) -> AttachmentView {
let width: CGFloat
switch hSize {
case .full:
width = bounds.width
case .half:
width = (bounds.width - 4) / 2
}
let height: CGFloat
switch vSize {
case .full:
height = bounds.height
case .half:
height = (bounds.height - 4) / 2
}
let size = CGSize(width: width, height: height)
let attachmentView = AttachmentView(attachment: attachments[index], index: index)
attachmentView.delegate = delegate
attachmentView.translatesAutoresizingMaskIntoConstraints = false
@ -292,6 +274,8 @@ class AttachmentsContainerView: UIView {
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.font = .preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
label.text = "Sensitive Content"
let stack = UIStackView(arrangedSubviews: [
imageView,

View File

@ -86,20 +86,24 @@ extension BaseEmojiLabel {
func buildStringWithEmojisReplaced(usePlaceholders: Bool) -> NSAttributedString {
let mutAttrString = NSMutableAttributedString(attributedString: attributedString)
// lock once for the entire loop, rather than lock/unlocking for each iteration to do the lookup
// OSAllocatedUnfairLock.withLock expects a @Sendable closure, so this warns about captures of non-sendable types (attribute dstrings, text checking results)
// even though the closures is invoked on the same thread that withLock is called, so it's unclear why it needs to be @Sendable (FB11494878)
// so, just ignore the warnings
emojiImages.withLock { emojiImages in
let emojiAttachments = emojiImages.withLock {
$0.mapValues { image in
NSTextAttachment(emojiImage: image, in: self.emojiFont, with: self.emojiTextColor)
}
}
let placeholder = usePlaceholders ? NSTextAttachment(emojiPlaceholderIn: self.emojiFont) : nil
// replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis
for match in matches.reversed() {
let shortcode = (attributedString.string as NSString).substring(with: match.range(at: 1))
let attachment: NSTextAttachment
if let emojiImage = emojiImages[shortcode] {
attachment = NSTextAttachment(emojiImage: emojiImage, in: self.emojiFont, with: self.emojiTextColor)
if let emoji = emojiAttachments[shortcode] {
attachment = emoji
} else if usePlaceholders {
attachment = NSTextAttachment(emojiPlaceholderIn: self.emojiFont)
attachment = placeholder!
} else {
continue
}
@ -107,7 +111,6 @@ extension BaseEmojiLabel {
let attachmentStr = NSAttributedString(attachment: attachment)
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
}
}
return mutAttrString
}

View File

@ -32,6 +32,8 @@ class ConfirmLoadMoreCollectionViewCell: UICollectionViewCell {
let label = UILabel()
label.text = "Infinite scrolling is off. Do you want to keep going?"
label.font = .preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
label.textColor = .secondaryLabel
label.textAlignment = .natural
label.numberOfLines = 0

View File

@ -1,49 +0,0 @@
//
// ConfirmLoadMoreTableViewCell.swift
// Tusker
//
// Created by Shadowfacts on 6/23/21.
// Copyright © 2021 Shadowfacts. All rights reserved.
//
import UIKit
protocol ConfirmLoadOlderTableViewCellDelegate: AnyObject {
func confirmLoadMore()
}
class ConfirmLoadMoreTableViewCell: UITableViewCell {
var confirmLoadMore: (() -> Void)?
@IBOutlet weak var confirmButton: UIButton!
private var isLoading = false
override func awakeFromNib() {
super.awakeFromNib()
var config = UIButton.Configuration.tinted()
config.title = "Load More"
config.showsActivityIndicator = false
config.imagePadding = 4
confirmButton.configuration = config
confirmButton.configurationUpdateHandler = { [unowned self] button in
button.configuration?.showsActivityIndicator = self.isLoading
}
}
override func prepareForReuse() {
super.prepareForReuse()
isLoading = false
confirmButton.setNeedsUpdateConfiguration()
}
@IBAction func loadMorePressed(_ sender: Any) {
confirmLoadMore?()
isLoading = true
confirmButton.setNeedsUpdateConfiguration()
}
}

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19115.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19107.5"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="105" id="KGk-i7-Jjw" customClass="ConfirmLoadMoreTableViewCell" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="105"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="105"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="Rpx-45-c2n">
<rect key="frame" x="16" y="11" width="288" height="86"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Infinite scrolling is off. Do you want to keep going?" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9nv-Re-5sL">
<rect key="frame" x="0.0" y="0.0" width="288" height="41"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NT9-ly-efr">
<rect key="frame" x="0.0" y="45" width="288" height="41"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="tinted" title="Load More"/>
<connections>
<action selector="loadMorePressed:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Pgz-MB-icB"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
<constraints>
<constraint firstItem="Rpx-45-c2n" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="YfZ-rr-Omf"/>
<constraint firstItem="Rpx-45-c2n" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="hhi-yX-Wa4"/>
<constraint firstAttribute="trailingMargin" secondItem="Rpx-45-c2n" secondAttribute="trailing" id="jI8-St-34M"/>
<constraint firstAttribute="bottom" secondItem="Rpx-45-c2n" secondAttribute="bottom" constant="8" id="mQh-0l-Eo2"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="confirmButton" destination="NT9-ly-efr" id="Lja-th-LeH"/>
</connections>
<point key="canvasLocation" x="131.8840579710145" y="150.33482142857142"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="secondarySystemBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@ -17,6 +17,13 @@ class DraftTableViewCell: UITableViewCell {
@IBOutlet weak var attachmentsStackViewContainer: UIView!
@IBOutlet weak var attachmentsStackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
contentWarningLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))
contentWarningLabel.adjustsFontForContentSizeCategory = true
}
func updateUI(for draft: Draft) {
contentWarningLabel.text = draft.contentWarning
contentWarningLabel.isHidden = !draft.contentWarningEnabled

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -16,27 +18,27 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="gaD-3B-qO1">
<rect key="frame" x="15" y="11" width="352" height="124"/>
<rect key="frame" x="16" y="11" width="351" height="124"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Content Warning" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhS-ig-6Fu">
<rect key="frame" x="0.0" y="0.0" width="352" height="18"/>
<rect key="frame" x="0.0" y="0.0" width="351" height="18"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zMS-88-DcM">
<rect key="frame" x="0.0" y="26" width="352" height="40"/>
<rect key="frame" x="0.0" y="26" width="351" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" ambiguous="YES" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8eA-yd-rBp">
<rect key="frame" x="0.0" y="0.0" width="311.5" height="32"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" ambiguous="YES" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8eA-yd-rBp">
<rect key="frame" x="0.0" y="0.0" width="310.5" height="32"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D2X-9O-iQw">
<rect key="frame" x="327.5" y="0.0" width="24.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D2X-9O-iQw">
<rect key="frame" x="326.5" y="0.0" width="24.5" height="20.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@ -50,7 +52,7 @@
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="csc-gx-KVg">
<rect key="frame" x="0.0" y="74" width="352" height="50"/>
<rect key="frame" x="0.0" y="74" width="351" height="50"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="htC-hf-vJ4">
<rect key="frame" x="0.0" y="0.0" width="352" height="50"/>
@ -88,4 +90,9 @@
<point key="canvasLocation" x="-388" y="184.85757121439281"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@ -28,8 +28,14 @@ class InstanceTableViewCell: UITableViewCell {
thumbnailImageView.layer.masksToBounds = true
thumbnailImageView.layer.cornerRadius = 5
domainLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 22, weight: .bold))
domainLabel.adjustsFontForContentSizeCategory = true
adultLabel.layer.masksToBounds = true
adultLabel.layer.cornerRadius = 0.5 * adultLabel.bounds.height
descriptionTextView.defaultFont = .preferredFont(forTextStyle: .body)
descriptionTextView.adjustsFontForContentSizeCategory = true
}
func updateUI(instance: InstanceSelector.Instance) {

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -27,34 +29,34 @@
</constraints>
</imageView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="QG1-xB-nmt">
<rect key="frame" x="88" y="0.0" width="200" height="63"/>
<rect key="frame" x="88" y="0.0" width="200" height="64.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="XtJ-BL-iHb">
<rect key="frame" x="0.0" y="0.0" width="200" height="26.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="domain.tld" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SjP-Nk-sSH">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="domain.tld" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SjP-Nk-sSH">
<rect key="frame" x="0.0" y="0.0" width="164" height="26.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="18+" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ekk-aL-7Pq">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="18+" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ekk-aL-7Pq">
<rect key="frame" x="164" y="1.5" width="36" height="24"/>
<color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="systemBlueColor"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="CNa-UL-LJh"/>
<constraint firstAttribute="width" constant="36" id="tzn-KZ-r2f"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Instance Description" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Z5t-Zl-040" customClass="ContentTextView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="26.5" width="200" height="36.5"/>
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Instance Description" textAlignment="natural" adjustsFontForContentSizeCategory="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Z5t-Zl-040" customClass="ContentTextView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="26.5" width="200" height="38"/>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
@ -79,4 +81,12 @@
<point key="canvasLocation" x="131.8840579710145" y="178.79464285714286"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="labelColor">
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBlueColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@ -10,9 +10,17 @@ import UIKit
class LinkTextView: UITextView {
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
delaysContentTouches = false
isScrollEnabled = false
isEditable = false

View File

@ -35,6 +35,13 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
actionLabel.combiner = self.updateActionLabel
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -40,15 +41,15 @@
</label>
</subviews>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Actioned by Person 1, Person 2, and Person 3" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fkn-Gk-ngr" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="34" width="230" height="41"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Actioned by Person 1, Person 2, and Person 3" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fkn-Gk-ngr" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="34" width="230" height="42.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc7-zZ-HrZ">
<rect key="frame" x="0.0" y="79" width="230" height="74"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc7-zZ-HrZ">
<rect key="frame" x="0.0" y="80.5" width="230" height="72.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>

View File

@ -31,6 +31,13 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
actionLabel.combiner = self.updateActionLabel
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18121" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18091"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -40,9 +41,9 @@
</label>
</subviews>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Followed by Person 1 and Person 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHA-9x-pcO" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Followed by Person 1 and Person 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHA-9x-pcO" customClass="MultiSourceEmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="30" width="230" height="46"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
@ -75,7 +76,7 @@
</tableViewCell>
</objects>
<resources>
<image name="person.fill.badge.plus" catalog="system" width="128" height="124"/>
<image name="person.fill.badge.plus" catalog="system" width="128" height="125"/>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>

View File

@ -36,6 +36,13 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
avatarImageView.layer.masksToBounds = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -41,9 +42,9 @@
</label>
</subviews>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Request to follow by Person 1" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aM6-C6-9QH" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Request to follow by Person 1" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aM6-C6-9QH" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="34" width="230" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
@ -104,9 +105,9 @@
</tableViewCell>
</objects>
<resources>
<image name="checkmark.circle.fill" catalog="system" width="128" height="121"/>
<image name="checkmark.circle.fill" catalog="system" width="128" height="123"/>
<image name="person.fill" catalog="system" width="128" height="120"/>
<image name="xmark.circle.fill" catalog="system" width="128" height="121"/>
<image name="xmark.circle.fill" catalog="system" width="128" height="123"/>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>

View File

@ -24,6 +24,20 @@ class PollFinishedTableViewCell: UITableViewCell {
private var updateTimestampWorkItem: DispatchWorkItem?
override func awakeFromNib() {
super.awakeFromNib()
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
displayNameLabel.font = .preferredFont(forTextStyle: .body).withTraits(.traitBold)!
displayNameLabel.adjustsFontForContentSizeCategory = true
}
func updateUI(notification: Pachyderm.Notification) {
guard let statusID = notification.status?.id,
let status = delegate?.apiController.persistentContainer.status(for: statusID),

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -23,9 +24,9 @@
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="69j-GL-yd7">
<rect key="frame" x="0.0" y="0.0" width="232" height="20.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A poll has finished" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9He-JX-i6Z">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A poll has finished" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9He-JX-i6Z">
<rect key="frame" x="0.0" y="0.0" width="208" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
@ -43,9 +44,9 @@
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bLL-8K-VWn">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bLL-8K-VWn">
<rect key="frame" x="0.0" y="49" width="232" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
@ -56,7 +57,7 @@
</subviews>
</stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark.square.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cqi-cV-ejs">
<rect key="frame" x="34" y="10" width="30" height="32"/>
<rect key="frame" x="34" y="9" width="30" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="E9e-iF-rqo"/>
<constraint firstAttribute="width" constant="30" id="Efu-VP-pjH"/>

View File

@ -0,0 +1,160 @@
//
// ProfileFieldsView.swift
// Tusker
//
// Created by Shadowfacts on 11/4/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import UIKit
class ProfileFieldsView: UIView {
weak var delegate: ProfileHeaderViewDelegate?
private let stack = UIStackView()
private var fieldViews: [(EmojiLabel, ContentTextView)] = []
private var fieldConstraints: [NSLayoutConstraint] = []
private var isUsingSingleColumn: Bool = false
private var needsSingleColumn: Bool {
traitCollection.preferredContentSizeCategory > .large
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
stack.axis = .vertical
stack.alignment = .fill
stack.translatesAutoresizingMaskIntoConstraints = false
addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: leadingAnchor),
stack.trailingAnchor.constraint(equalTo: trailingAnchor),
stack.topAnchor.constraint(equalTo: topAnchor),
stack.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if isUsingSingleColumn != needsSingleColumn {
configureFields()
}
}
func updateUI(account: AccountMO) {
isHidden = account.fields.isEmpty
guard !account.fields.isEmpty else {
return
}
for (name, value) in fieldViews {
name.removeFromSuperview()
value.removeFromSuperview()
}
fieldViews = []
for field in account.fields {
let nameLabel = EmojiLabel()
nameLabel.text = field.name
nameLabel.font = .preferredFont(forTextStyle: .body).withTraits(.traitBold)!
nameLabel.adjustsFontForContentSizeCategory = true
nameLabel.numberOfLines = 0
nameLabel.lineBreakMode = .byWordWrapping
nameLabel.setEmojis(account.emojis, identifier: account.id)
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
let valueTextView = ContentTextView()
valueTextView.isSelectable = false
valueTextView.defaultFont = .preferredFont(forTextStyle: .body)
valueTextView.adjustsFontForContentSizeCategory = true
valueTextView.setTextFromHtml(field.value)
valueTextView.setEmojis(account.emojis, identifier: account.id)
valueTextView.navigationDelegate = delegate
valueTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
fieldViews.append((nameLabel, valueTextView))
}
configureFields()
}
@objc private func configureFields() {
guard !isHidden else {
return
}
isUsingSingleColumn = needsSingleColumn
NSLayoutConstraint.deactivate(fieldConstraints)
fieldConstraints = []
stack.arrangedSubviews.forEach { $0.removeFromSuperview() }
if needsSingleColumn {
stack.spacing = 4
var isFirst = true
for (name, value) in fieldViews {
if isFirst {
isFirst = false
} else {
let spacer = UIView()
// don't need any height, since there's 4pts of padding on either side
spacer.heightAnchor.constraint(equalToConstant: 0).isActive = true
stack.addArrangedSubview(spacer)
}
name.textAlignment = .natural
stack.addArrangedSubview(name)
value.textAlignment = .natural
stack.addArrangedSubview(value)
}
} else {
stack.spacing = 8
let dividerLayoutGuide = UILayoutGuide()
addLayoutGuide(dividerLayoutGuide)
fieldConstraints.append(contentsOf: [
dividerLayoutGuide.widthAnchor.constraint(equalToConstant: 8),
])
for (name, value) in fieldViews {
name.textAlignment = .right
name.translatesAutoresizingMaskIntoConstraints = false
value.textAlignment = .left
value.translatesAutoresizingMaskIntoConstraints = false
let fieldContainer = UIView()
fieldContainer.addSubview(name)
fieldContainer.addSubview(value)
stack.addArrangedSubview(fieldContainer)
fieldConstraints.append(contentsOf: [
name.leadingAnchor.constraint(equalTo: fieldContainer.leadingAnchor),
name.trailingAnchor.constraint(equalTo: dividerLayoutGuide.leadingAnchor),
name.topAnchor.constraint(equalTo: fieldContainer.topAnchor),
name.bottomAnchor.constraint(equalTo: fieldContainer.bottomAnchor),
value.leadingAnchor.constraint(equalTo: dividerLayoutGuide.trailingAnchor),
value.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor),
value.topAnchor.constraint(equalTo: fieldContainer.topAnchor),
value.bottomAnchor.constraint(equalTo: fieldContainer.bottomAnchor),
name.widthAnchor.constraint(greaterThanOrEqualTo: value.widthAnchor, multiplier: 0.5),
name.widthAnchor.constraint(lessThanOrEqualTo: value.widthAnchor, multiplier: 2),
])
}
}
NSLayoutConstraint.activate(fieldConstraints)
}
}

View File

@ -36,9 +36,7 @@ class ProfileHeaderView: UIView {
@IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var followsYouLabel: UILabel!
@IBOutlet weak var noteTextView: StatusContentTextView!
@IBOutlet weak var fieldsStackView: UIStackView!
@IBOutlet weak var fieldNamesStackView: UIStackView!
@IBOutlet weak var fieldValuesStackView: UIStackView!
@IBOutlet weak var fieldsView: ProfileFieldsView!
@IBOutlet weak var pagesSegmentedControl: UISegmentedControl!
var accountID: String!
@ -68,15 +66,31 @@ class ProfileHeaderView: UIView {
moreButton.layer.cornerRadius = 16
moreButton.layer.masksToBounds = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
moreButton.addInteraction(UIPointerInteraction(delegate: self))
moreButton.showsMenuAsPrimaryAction = true
moreButton.isContextMenuInteractionEnabled = true
displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold))
displayNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .light))
usernameLabel.adjustsFontForContentSizeCategory = true
followsYouLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 14))
followsYouLabel.adjustsFontForContentSizeCategory = true
noteTextView.defaultFont = .preferredFont(forTextStyle: .body)
noteTextView.adjustsFontForContentSizeCategory = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
}
private func createObservers() {
// mastodonController may be nil if the ProfileViewController is deinit'd before the header is even created
guard let mastodonController else {
return
}
cancellables = []
mastodonController.persistentContainer.accountSubject
@ -128,43 +142,14 @@ class ProfileHeaderView: UIView {
}
}
fieldsStackView.isHidden = account.fields.isEmpty
fieldNamesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
fieldValuesStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
var fieldAccessibilityElements = [Any]()
for field in account.fields {
let nameLabel = EmojiLabel()
nameLabel.text = field.name
nameLabel.font = .boldSystemFont(ofSize: 17)
nameLabel.textAlignment = .right
nameLabel.numberOfLines = 0
nameLabel.lineBreakMode = .byWordWrapping
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
nameLabel.setEmojis(account.emojis, identifier: "")
fieldNamesStackView.addArrangedSubview(nameLabel)
let valueTextView = ContentTextView()
valueTextView.isSelectable = false
valueTextView.font = .systemFont(ofSize: 17)
valueTextView.setTextFromHtml(field.value)
valueTextView.setEmojis(account.emojis, identifier: account.id)
valueTextView.textAlignment = .left
valueTextView.awakeFromNib()
valueTextView.navigationDelegate = delegate
valueTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
fieldValuesStackView.addArrangedSubview(valueTextView)
nameLabel.heightAnchor.constraint(equalTo: valueTextView.heightAnchor).isActive = true
fieldAccessibilityElements.append(nameLabel)
fieldAccessibilityElements.append(valueTextView)
}
fieldsView.updateUI(account: account)
accessibilityElements = [
displayNameLabel!,
usernameLabel!,
noteTextView!,
] + fieldAccessibilityElements + [
// TODO: voiceover for fieldsview
// fieldsView!,
moreButton!,
pagesSegmentedControl!,
]

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18121" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18091"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -14,13 +15,13 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dgG-dR-lSv">
<rect key="frame" x="0.0" y="44" width="414" height="150"/>
<rect key="frame" x="0.0" y="48" width="414" height="150"/>
<constraints>
<constraint firstAttribute="height" constant="150" id="aCE-CA-XWm"/>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wT9-2J-uSY">
<rect key="frame" x="16" y="134" width="120" height="120"/>
<rect key="frame" x="16" y="138" width="120" height="120"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TkY-oK-if4">
<rect key="frame" x="2" y="2" width="116" height="116"/>
@ -38,14 +39,14 @@
<constraint firstItem="TkY-oK-if4" firstAttribute="centerX" secondItem="wT9-2J-uSY" secondAttribute="centerX" id="ozz-sa-gSc"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vcl-Gl-kXl" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="144" y="202" width="254" height="24"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vcl-Gl-kXl" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="144" y="206" width="254" height="32"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRJ-Xf-kc9" customClass="VisualEffectImageButton" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="374" y="154" width="32" height="32"/>
<rect key="frame" x="374" y="158" width="32" height="32"/>
<viewLayoutGuide key="safeArea" id="kQa-ou-pQz"/>
<accessibility key="accessibilityConfiguration" label="More Actions">
<accessibilityTraits key="traits" button="YES"/>
@ -59,7 +60,7 @@
</userDefinedRuntimeAttributes>
</view>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="u4P-3i-gEq">
<rect key="frame" x="16" y="262" width="398" height="600"/>
<rect key="frame" x="16" y="419" width="398" height="443"/>
<subviews>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Follows you" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UF8-nI-KVj">
<rect key="frame" x="0.0" y="0.0" width="75.5" height="0.0"/>
@ -68,29 +69,21 @@
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1O8-2P-Gbf" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="382" height="186.5"/>
<rect key="frame" x="0.0" y="0.0" width="382" height="259.5"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" distribution="fillProportionally" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="sp4-Zb-00B">
<rect key="frame" x="0.0" y="194.5" width="382" height="358"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="aqG-FA-so5">
<rect key="frame" x="0.0" y="0.0" width="186.5" height="358"/>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="EfH-Dj-Jmn">
<rect key="frame" x="194.5" y="0.0" width="187.5" height="358"/>
</stackView>
</subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vKC-m1-Sbs" customClass="ProfileFieldsView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="267.5" width="398" height="128"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="EfH-Dj-Jmn" firstAttribute="width" relation="greaterThanOrEqual" secondItem="aqG-FA-so5" secondAttribute="width" multiplier="0.5" id="2hZ-pF-C9b"/>
<constraint firstItem="EfH-Dj-Jmn" firstAttribute="width" relation="lessThanOrEqual" secondItem="aqG-FA-so5" secondAttribute="width" multiplier="2" id="Lir-Ff-z0m"/>
<constraint firstAttribute="height" constant="128" placeholder="YES" id="xbR-M6-H0I"/>
</constraints>
</stackView>
</view>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="n1M-vM-Cj0">
<rect key="frame" x="0.0" y="560.5" width="382" height="32"/>
<rect key="frame" x="0.0" y="403.5" width="382" height="32"/>
<segments>
<segment title="Posts"/>
<segment title="Posts and Replies"/>
@ -101,7 +94,7 @@
</connections>
</segmentedControl>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5ja-fK-Fqz">
<rect key="frame" x="0.0" y="599.5" width="398" height="0.5"/>
<rect key="frame" x="0.0" y="442.5" width="398" height="0.5"/>
<color key="backgroundColor" systemColor="separatorColor"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="VwS-gV-q8M"/>
@ -109,14 +102,14 @@
</view>
</subviews>
<constraints>
<constraint firstItem="vKC-m1-Sbs" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" id="0dI-ax-7eI"/>
<constraint firstItem="n1M-vM-Cj0" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" constant="-16" id="9Ds-zl-acc"/>
<constraint firstItem="sp4-Zb-00B" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" constant="-16" id="Qum-qT-goH"/>
<constraint firstItem="5ja-fK-Fqz" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" id="azv-le-93y"/>
<constraint firstItem="1O8-2P-Gbf" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" constant="-16" id="hnA-3G-B9B"/>
</constraints>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1C3-Pd-QiL">
<rect key="frame" x="144" y="234" width="254" height="18"/>
<rect key="frame" x="144" y="238" width="254" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
@ -132,25 +125,24 @@
<constraint firstItem="vcl-Gl-kXl" firstAttribute="leading" secondItem="wT9-2J-uSY" secondAttribute="trailing" constant="8" id="8ho-WU-MxW"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="u4P-3i-gEq" secondAttribute="bottom" id="9zc-N2-mfI"/>
<constraint firstItem="bRJ-Xf-kc9" firstAttribute="bottom" secondItem="dgG-dR-lSv" secondAttribute="bottom" constant="-8" id="AXS-bG-20Q"/>
<constraint firstItem="1C3-Pd-QiL" firstAttribute="bottom" secondItem="TkY-oK-if4" secondAttribute="bottom" id="OpB-YM-gyu"/>
<constraint firstItem="1C3-Pd-QiL" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="TkY-oK-if4" secondAttribute="bottom" id="OpB-YM-gyu"/>
<constraint firstItem="dgG-dR-lSv" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="VD1-yc-KSa"/>
<constraint firstItem="wT9-2J-uSY" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="WNS-AR-ff2"/>
<constraint firstItem="bRJ-Xf-kc9" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" constant="-8" id="ZB4-ys-9zP"/>
<constraint firstItem="1C3-Pd-QiL" firstAttribute="top" relation="greaterThanOrEqual" secondItem="vcl-Gl-kXl" secondAttribute="bottom" id="d0z-X6-Sig"/>
<constraint firstItem="1C3-Pd-QiL" firstAttribute="top" secondItem="vcl-Gl-kXl" secondAttribute="bottom" id="d0z-X6-Sig"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="vcl-Gl-kXl" secondAttribute="trailing" constant="16" id="e38-Od-kPg"/>
<constraint firstItem="u4P-3i-gEq" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="hgl-UR-o3W"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="dgG-dR-lSv" secondAttribute="trailing" id="j0d-hY-815"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="1C3-Pd-QiL" secondAttribute="trailing" constant="16" id="pcH-vi-2zH"/>
<constraint firstAttribute="trailing" secondItem="u4P-3i-gEq" secondAttribute="trailing" id="ph6-NT-A02"/>
<constraint firstItem="u4P-3i-gEq" firstAttribute="top" secondItem="wT9-2J-uSY" secondAttribute="bottom" constant="8" id="tKQ-6d-Z55"/>
<constraint firstItem="u4P-3i-gEq" firstAttribute="top" relation="greaterThanOrEqual" secondItem="wT9-2J-uSY" secondAttribute="bottom" constant="8" id="tKQ-6d-Z55"/>
<constraint firstItem="u4P-3i-gEq" firstAttribute="top" relation="greaterThanOrEqual" secondItem="vcl-Gl-kXl" secondAttribute="bottom" constant="8" id="xDD-rx-gC0"/>
</constraints>
<connections>
<outlet property="avatarContainerView" destination="wT9-2J-uSY" id="yEm-h7-tfq"/>
<outlet property="avatarImageView" destination="TkY-oK-if4" id="bSJ-7z-j4w"/>
<outlet property="displayNameLabel" destination="vcl-Gl-kXl" id="64n-a9-my0"/>
<outlet property="fieldNamesStackView" destination="aqG-FA-so5" id="prA-n7-blZ"/>
<outlet property="fieldValuesStackView" destination="EfH-Dj-Jmn" id="LMk-Hn-EkY"/>
<outlet property="fieldsStackView" destination="sp4-Zb-00B" id="eyx-GF-2Wf"/>
<outlet property="fieldsView" destination="vKC-m1-Sbs" id="FeE-jh-lYH"/>
<outlet property="followsYouLabel" destination="UF8-nI-KVj" id="dTe-DQ-eJV"/>
<outlet property="headerImageView" destination="dgG-dR-lSv" id="HXT-v4-2iX"/>
<outlet property="moreButton" destination="bRJ-Xf-kc9" id="zIN-pz-L7y"/>
@ -164,7 +156,7 @@
<resources>
<image name="ellipsis" catalog="system" width="128" height="37"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>

View File

@ -182,7 +182,8 @@ class BaseStatusTableViewCell: UITableViewCell {
updateStatusIconsForPreferences(status)
if state.unknown {
state.resolveFor(status: status, text: contentTextView.text)
layoutIfNeeded()
state.resolveFor(status: status, height: contentTextView.bounds.height)
if state.collapsible! && showStatusAutomatically {
state.collapsed = false
}
@ -230,7 +231,17 @@ class BaseStatusTableViewCell: UITableViewCell {
func updateUIForPreferences(account: AccountMO, status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || status.sensitive
if Preferences.shared.blurAllMedia {
attachmentsView.contentHidden = true
} else if status.sensitive {
if !Preferences.shared.blurMediaBehindContentWarning && !status.spoilerText.isEmpty {
attachmentsView.contentHidden = false
} else {
attachmentsView.contentHidden = true
}
} else {
attachmentsView.contentHidden = false
}
updateStatusIconsForPreferences(status)

View File

@ -49,16 +49,35 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
moreButton!,
]
contentTextView.defaultFont = .systemFont(ofSize: 18)
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold))
displayNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 17, weight: .light))
usernameLabel.adjustsFontForContentSizeCategory = true
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly]
metaIndicatorsView.squeezeHorizontal = true
metaIndicatorsView.primaryAxis = .horizontal
contentWarningLabel.font = .preferredFont(forTextStyle: .body).withTraits(.traitBold)!
contentWarningLabel.adjustsFontForContentSizeCategory = true
contentTextView.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 18))
contentTextView.adjustsFontForContentSizeCategory = true
contentTextView.dataDetectorTypes = [.flightNumber, .address, .shipmentTrackingNumber, .phoneNumber]
if #available(iOS 16.0, *) {
contentTextView.dataDetectorTypes.formUnion([.money, .physicalValue])
}
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly]
metaIndicatorsView.squeezeHorizontal = true
let metaFont = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 15))
totalFavoritesButton.titleLabel!.font = metaFont
totalFavoritesButton.titleLabel!.adjustsFontForContentSizeCategory = true
totalReblogsButton.titleLabel!.font = metaFont
totalReblogsButton.titleLabel!.adjustsFontForContentSizeCategory = true
timestampAndClientLabel.font = metaFont
timestampAndClientLabel.adjustsFontForContentSizeCategory = true
}
override func doUpdateUI(status: StatusMO, state: StatusState) {

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@ -261,13 +261,13 @@
</view>
</objects>
<resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
<image name="chevron.down" catalog="system" width="128" height="72"/>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/>
<image name="chevron.down" catalog="system" width="128" height="70"/>
<image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="repeat" catalog="system" width="128" height="98"/>
<image name="star.fill" catalog="system" width="128" height="116"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

View File

@ -51,10 +51,12 @@ class StatusCardView: UIView {
titleLabel = UILabel()
titleLabel.font = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!, size: 0)
titleLabel.adjustsFontForContentSizeCategory = true
titleLabel.numberOfLines = 2
descriptionLabel = UILabel()
descriptionLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .caption1), size: 0)
descriptionLabel.adjustsFontForContentSizeCategory = true
descriptionLabel.numberOfLines = 2
descriptionLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical)

View File

@ -104,7 +104,9 @@ extension StatusCollectionViewCell {
favoriteButton.isEnabled = mastodonController.loggedIn
if statusState.unknown {
statusState.resolveFor(status: status, text: contentContainer.contentTextView.text)
// layout so that we can take the content height into consideration when deciding whether to collapse
layoutIfNeeded()
statusState.resolveFor(status: status, height: contentContainer.contentTextView.bounds.height)
if statusState.collapsible! && showStatusAutomatically {
statusState.collapsed = false
}
@ -145,7 +147,17 @@ extension StatusCollectionViewCell {
func baseUpdateUIForPreferences(status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.avatarImageViewSize
contentContainer.attachmentsView.contentHidden = Preferences.shared.blurAllMedia || status.sensitive
if Preferences.shared.blurAllMedia {
contentContainer.attachmentsView.contentHidden = true
} else if status.sensitive {
if !Preferences.shared.blurMediaBehindContentWarning && !status.spoilerText.isEmpty {
contentContainer.attachmentsView.contentHidden = false
} else {
contentContainer.attachmentsView.contentHidden = true
}
} else {
contentContainer.attachmentsView.contentHidden = false
}
let reblogButtonImage: UIImage
if Preferences.shared.alwaysShowStatusVisibilityIcon || reblogEnabled(status: status) {

View File

@ -11,7 +11,8 @@ import UIKit
class StatusContentContainer: UIView {
let contentTextView = StatusContentTextView().configure {
$0.defaultFont = .systemFont(ofSize: 16)
$0.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 16))
$0.adjustsFontForContentSizeCategory = true
$0.isScrollEnabled = false
$0.backgroundColor = nil
$0.isEditable = false

View File

@ -13,11 +13,54 @@ class StatusMetaIndicatorsView: UIView {
var allowedIndicators: Indicator = .all
var squeezeHorizontal = false
// The axis in which the indicators grow
var primaryAxis: NSLayoutConstraint.Axis = .vertical
// Only used when using single axis mode
var secondaryAxisAlignment: Alignment = .leading
private var images: [UIImageView] = []
private var isUsingSingleAxis = false
private var needsSingleAxis: Bool {
traitCollection.preferredContentSizeCategory > .extraLarge
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
NotificationCenter.default.addObserver(self, selector: #selector(configureImageViews), name: UIAccessibility.boldTextStatusDidChangeNotification, object: nil)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if isUsingSingleAxis != needsSingleAxis {
for image in images {
configureImageView(image)
}
placeImageViews(images)
}
}
@objc private func configureImageViews() {
for image in images {
configureImageView(image)
}
}
private func configureImageView(_ imageView: UIImageView) {
let weight: UIImage.SymbolWeight = UIAccessibility.isBoldTextEnabled ? .regular : traitCollection.preferredContentSizeCategory > .large ? .light : .thin
let scale: UIImage.SymbolScale = traitCollection.preferredContentSizeCategory > .extraLarge ? .large : .default
imageView.preferredSymbolConfiguration = .init(pointSize: 0, weight: weight, scale: scale)
}
func updateUI(status: StatusMO) {
images.forEach { $0.removeFromSuperview() }
var images: [UIImage] = []
if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil {
@ -32,13 +75,66 @@ class StatusMetaIndicatorsView: UIView {
images.append(UIImage(named: "link.broken")!)
}
self.images = []
for (index, image) in images.enumerated() {
let v = UIImageView(image: image)
let views = images.map {
let v = UIImageView(image: $0)
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFit
v.tintColor = .secondaryLabel
v.preferredSymbolConfiguration = .init(weight: .thin)
configureImageView(v)
return v
}
placeImageViews(views)
}
private func placeImageViews(_ imageViews: [UIImageView]) {
images.forEach { $0.removeFromSuperview() }
images = imageViews
guard !images.isEmpty else {
return
}
isUsingSingleAxis = needsSingleAxis
if needsSingleAxis {
for v in images {
addSubview(v)
switch (primaryAxis, secondaryAxisAlignment) {
case (.horizontal, .leading):
v.topAnchor.constraint(equalTo: topAnchor).isActive = true
case (.horizontal, .trailing):
v.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
case (.vertical, .leading):
v.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
case (.vertical, .trailing):
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
case (_, _):
fatalError()
}
}
if primaryAxis == .vertical {
images.first!.topAnchor.constraint(equalTo: topAnchor).isActive = true
images.last!.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
} else {
images.first!.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
images.last!.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
for (a, b) in zip(images, images.dropFirst()) {
if primaryAxis == .vertical {
b.topAnchor.constraint(equalTo: a.bottomAnchor, constant: 4).isActive = true
} else {
b.leadingAnchor.constraint(equalTo: a.trailingAnchor, constant: 4).isActive = true
}
}
return
}
guard primaryAxis == .vertical || imageViews.count <= 2 else {
fatalError("StatusMetaIndicatorsView does not support horizontal primary axis with more than 2 views yet")
}
for (index, v) in images.enumerated() {
addSubview(v)
if index % 2 == 0 {
@ -64,14 +160,9 @@ class StatusMetaIndicatorsView: UIView {
v.topAnchor.constraint(equalTo: self.images[index - 1].bottomAnchor, constant: 4).isActive = true
}
v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
self.images.append(v)
}
}
}
extension StatusMetaIndicatorsView {
struct Indicator: OptionSet {
let rawValue: Int
@ -81,4 +172,8 @@ extension StatusMetaIndicatorsView {
static let all: Indicator = [.reply, .visibility, .localOnly]
}
enum Alignment {
case leading, trailing
}
}

View File

@ -18,6 +18,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
private lazy var reblogLabel = EmojiLabel().configure {
$0.textColor = .secondaryLabel
$0.font = .preferredFont(forTextStyle: .body)
$0.adjustsFontForContentSizeCategory = true
// this needs to have a higher priorty than the content container's zero height constraint
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.isUserInteractionEnabled = true
@ -58,7 +60,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
}
private let metaIndicatorsView = StatusMetaIndicatorsView()
private let metaIndicatorsView = StatusMetaIndicatorsView().configure {
$0.primaryAxis = .vertical
$0.secondaryAxisAlignment = .trailing
}
private lazy var contentVStack = UIStackView(arrangedSubviews: [
nameHStack,
@ -87,6 +92,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
$0.setContentHuggingPriority(.init(251), for: .horizontal)
$0.setContentCompressionResistancePriority(.init(749), for: .horizontal)
}
@ -98,6 +104,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
$0.setContentHuggingPriority(.init(249), for: .horizontal)
$0.setContentCompressionResistancePriority(.init(748), for: .horizontal)
}
@ -114,6 +121,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
}
private(set) lazy var contentWarningLabel = EmojiLabel().configure {
@ -124,6 +132,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold.rawValue,
]
]), size: 0)
$0.adjustsFontForContentSizeCategory = true
// this needs to have a higher priorty than the content container's zero height constraint
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.isUserInteractionEnabled = true

View File

@ -46,17 +46,51 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
isAccessibilityElement = true
reblogLabel.font = .preferredFont(forTextStyle: .body)
reblogLabel.adjustsFontForContentSizeCategory = true
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
isAccessibilityElement = true
avatarImageView.addInteraction(UIContextMenuInteraction(delegate: self))
displayNameLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold.rawValue,
]
]), size: 0)
displayNameLabel.adjustsFontForContentSizeCategory = true
usernameLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
usernameLabel.adjustsFontForContentSizeCategory = true
timestampLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
]
]), size: 0)
timestampLabel.adjustsFontForContentSizeCategory = true
metaIndicatorsView.primaryAxis = .vertical
metaIndicatorsView.secondaryAxisAlignment = .trailing
contentWarningLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
.traits: [
UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold.rawValue,
]
]), size: 0)
contentWarningLabel.adjustsFontForContentSizeCategory = true
contentTextView.defaultFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 16))
contentTextView.adjustsFontForContentSizeCategory = true
// todo: double check this on RTL layouts
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
contentTextView.defaultFont = .systemFont(ofSize: 16)
avatarImageView.addInteraction(UIContextMenuInteraction(delegate: self))
updateActionsVisibility()
}

View File

@ -16,20 +16,20 @@ protocol ToastableViewController: UIViewController {
}
private var currentToastKey = "Tusker_currentToast"
private let currentToastKey = UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: 0)
extension ToastableViewController {
private(set) var currentToast: ToastView? {
get {
let holder = objc_getAssociatedObject(self, &currentToastKey) as? WeakHolder<ToastView>
let holder = objc_getAssociatedObject(self, currentToastKey) as? WeakHolder<ToastView>
return holder?.object
}
set {
if let newValue = newValue {
objc_setAssociatedObject(self, &currentToastKey, WeakHolder(object: newValue), .OBJC_ASSOCIATION_RETAIN)
objc_setAssociatedObject(self, currentToastKey, WeakHolder(object: newValue), .OBJC_ASSOCIATION_RETAIN)
} else {
objc_setAssociatedObject(self, &currentToastKey, nil, .OBJC_ASSOCIATION_RETAIN)
objc_setAssociatedObject(self, currentToastKey, nil, .OBJC_ASSOCIATION_RETAIN)
}
}
}