Compare commits
20 Commits
b38c24b347
...
68c3affacf
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 68c3affacf | |
Shadowfacts | e40f4faa8e | |
Shadowfacts | b56c6c37ec | |
Shadowfacts | 999118798c | |
Shadowfacts | 84cf755332 | |
Shadowfacts | 5bd7c0ad2b | |
Shadowfacts | 7fe06d42ce | |
Shadowfacts | 20986ba3f0 | |
Shadowfacts | 97a95c435e | |
Shadowfacts | b9555cf7dd | |
Shadowfacts | 590b9f0bcc | |
Shadowfacts | ca2ceaea56 | |
Shadowfacts | 96d8a79d42 | |
Shadowfacts | 11233f7d25 | |
Shadowfacts | a991e0f429 | |
Shadowfacts | bfdce07d81 | |
Shadowfacts | f5953655c5 | |
Shadowfacts | 6bc4993d81 | |
Shadowfacts | 68646c4b4d | |
Shadowfacts | 38b0d57118 |
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
@ -26,7 +50,7 @@ class ComposeDrawingViewController: UIViewController {
|
|||
private var toolPicker: PKToolPicker!
|
||||
|
||||
private var initialDrawing: PKDrawing?
|
||||
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
@ -45,7 +69,7 @@ class ComposeDrawingViewController: UIViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
overrideUserInterfaceStyle = .light
|
||||
|
||||
|
||||
navigationItem.title = NSLocalizedString("Draw", comment: "compose drawing screen title")
|
||||
cancelBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelPressed))
|
||||
undoBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrow.uturn.left.circle"), style: .plain, target: self, action: #selector(undoPressed))
|
||||
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -9,78 +9,164 @@
|
|||
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()
|
||||
|
||||
title = NSLocalizedString("Trending Posts", comment: "trending posts screen title")
|
||||
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
|
||||
|
||||
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
|
||||
dataSource = createDataSource()
|
||||
}
|
||||
|
||||
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()
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.statuses])
|
||||
snapshot.appendItems([.loadingIndicator])
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -28,6 +28,12 @@ class AccountTableViewCell: UITableViewCell {
|
|||
super.awakeFromNib()
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -86,28 +86,31 @@ 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
|
||||
// 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)
|
||||
} else if usePlaceholders {
|
||||
attachment = NSTextAttachment(emojiPlaceholderIn: self.emojiFont)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
let attachmentStr = NSAttributedString(attachment: attachment)
|
||||
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
|
||||
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 emoji = emojiAttachments[shortcode] {
|
||||
attachment = emoji
|
||||
} else if usePlaceholders {
|
||||
attachment = placeholder!
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
let attachmentStr = NSAttributedString(attachment: attachment)
|
||||
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
|
||||
}
|
||||
|
||||
return mutAttrString
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -9,10 +9,18 @@
|
|||
import UIKit
|
||||
|
||||
class LinkTextView: UITextView {
|
||||
|
||||
override init(frame: CGRect, textContainer: NSTextContainer?) {
|
||||
super.init(frame: frame, textContainer: textContainer)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
private func commonInit() {
|
||||
delaysContentTouches = false
|
||||
isScrollEnabled = false
|
||||
isEditable = false
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -23,6 +23,20 @@ class PollFinishedTableViewCell: UITableViewCell {
|
|||
var notification: Pachyderm.Notification?
|
||||
|
||||
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,
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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!,
|
||||
]
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -45,18 +45,52 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
|||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
||||
|
||||
isAccessibilityElement = true
|
||||
|
||||
// todo: double check this on RTL layouts
|
||||
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
|
||||
|
||||
contentTextView.defaultFont = .systemFont(ofSize: 16)
|
||||
reblogLabel.font = .preferredFont(forTextStyle: .body)
|
||||
reblogLabel.adjustsFontForContentSizeCategory = true
|
||||
reblogLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
||||
|
||||
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
|
||||
|
||||
updateActionsVisibility()
|
||||
}
|
||||
|
||||
|
|
|
@ -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, ¤tToastKey) as? WeakHolder<ToastView>
|
||||
let holder = objc_getAssociatedObject(self, currentToastKey) as? WeakHolder<ToastView>
|
||||
return holder?.object
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
objc_setAssociatedObject(self, ¤tToastKey, WeakHolder(object: newValue), .OBJC_ASSOCIATION_RETAIN)
|
||||
objc_setAssociatedObject(self, currentToastKey, WeakHolder(object: newValue), .OBJC_ASSOCIATION_RETAIN)
|
||||
} else {
|
||||
objc_setAssociatedObject(self, ¤tToastKey, nil, .OBJC_ASSOCIATION_RETAIN)
|
||||
objc_setAssociatedObject(self, currentToastKey, nil, .OBJC_ASSOCIATION_RETAIN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue