Compare commits

...

20 Commits

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

View File

@ -1,5 +1,20 @@
# Changelog # 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) ## 2022.1 (43)
Features/Improvements: Features/Improvements:
- Re-add undo scroll-to-top by tapping the status bar a second time - Re-add undo scroll-to-top by tapping the status bar a second time

View File

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

View File

@ -26,6 +26,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.persistentStoreCoordinator = self.persistentStoreCoordinator context.persistentStoreCoordinator = self.persistentStoreCoordinator
context.automaticallyMergesChangesFromParent = true context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context return context
}() }()
@ -33,6 +34,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.persistentStoreCoordinator = self.persistentStoreCoordinator context.persistentStoreCoordinator = self.persistentStoreCoordinator
context.automaticallyMergesChangesFromParent = true context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context return context
}() }()
@ -62,6 +64,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
} }
viewContext.automaticallyMergesChangesFromParent = true viewContext.automaticallyMergesChangesFromParent = true
viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext) NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext)
} }
@ -72,12 +75,27 @@ class MastodonCachePersistentStore: NSPersistentContainer {
} }
do { do {
try context.save() try context.save()
} catch { } catch let error as NSError {
logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)") logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)")
let crumb = Breadcrumb(level: .fatal, category: "PersistentStore") 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) SentrySDK.addBreadcrumb(crumb: crumb)
fatalError("Unable to save managed object context") fatalError("Unable to save managed object context: \(String(describing: error))")
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -45,12 +45,14 @@ class Preferences: Codable, ObservableObject {
self.hideActionsInTimeline = try container.decodeIfPresent(Bool.self, forKey: .hideActionsInTimeline) ?? false self.hideActionsInTimeline = try container.decodeIfPresent(Bool.self, forKey: .hideActionsInTimeline) ?? false
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility) 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.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
self.requireAttachmentDescriptions = try container.decode(Bool.self, forKey: .requireAttachmentDescriptions) self.requireAttachmentDescriptions = try container.decode(Bool.self, forKey: .requireAttachmentDescriptions)
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode) self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger) self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger)
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia) 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.automaticallyPlayGifs = try container.decode(Bool.self, forKey: .automaticallyPlayGifs)
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps) 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(hideActionsInTimeline, forKey: .hideActionsInTimeline)
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility) try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
try container.encode(defaultReplyVisibility, forKey: .defaultReplyVisibility)
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts) try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
try container.encode(requireAttachmentDescriptions, forKey: .requireAttachmentDescriptions) try container.encode(requireAttachmentDescriptions, forKey: .requireAttachmentDescriptions)
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode) try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
try container.encode(mentionReblogger, forKey: .mentionReblogger) try container.encode(mentionReblogger, forKey: .mentionReblogger)
try container.encode(blurAllMedia, forKey: .blurAllMedia) try container.encode(blurAllMedia, forKey: .blurAllMedia)
try container.encode(blurMediaBehindContentWarning, forKey: .blurMediaBehindContentWarning)
try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs) try container.encode(automaticallyPlayGifs, forKey: .automaticallyPlayGifs)
try container.encode(openLinksInApps, forKey: .openLinksInApps) try container.encode(openLinksInApps, forKey: .openLinksInApps)
@ -122,13 +126,21 @@ class Preferences: Codable, ObservableObject {
// MARK: Composing // MARK: Composing
@Published var defaultPostVisibility = Status.Visibility.public @Published var defaultPostVisibility = Status.Visibility.public
@Published var defaultReplyVisibility = ReplyVisibility.sameAsPost
@Published var automaticallySaveDrafts = true @Published var automaticallySaveDrafts = true
@Published var requireAttachmentDescriptions = false @Published var requireAttachmentDescriptions = false
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs @Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
@Published var mentionReblogger = false @Published var mentionReblogger = false
// MARK: Media // MARK: Media
@Published var blurAllMedia = false @Published var blurAllMedia = false {
didSet {
if blurAllMedia {
blurMediaBehindContentWarning = true
}
}
}
@Published var blurMediaBehindContentWarning = true
@Published var automaticallyPlayGifs = true @Published var automaticallyPlayGifs = true
// MARK: Behavior // MARK: Behavior
@ -164,12 +176,14 @@ class Preferences: Codable, ObservableObject {
case hideActionsInTimeline case hideActionsInTimeline
case defaultPostVisibility case defaultPostVisibility
case defaultReplyVisibility
case automaticallySaveDrafts case automaticallySaveDrafts
case requireAttachmentDescriptions case requireAttachmentDescriptions
case contentWarningCopyMode case contentWarningCopyMode
case mentionReblogger case mentionReblogger
case blurAllMedia case blurAllMedia
case blurMediaBehindContentWarning
case automaticallyPlayGifs case automaticallyPlayGifs
case openLinksInApps 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 {} extension UIUserInterfaceStyle: Codable {}

View File

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

View File

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

View File

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

View File

@ -14,6 +14,30 @@ protocol ComposeDrawingViewControllerDelegate: AnyObject {
func composeDrawingViewController(_ drawingController: ComposeDrawingViewController, saveDrawing drawing: PKDrawing) 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 { class ComposeDrawingViewController: UIViewController {
weak var delegate: ComposeDrawingViewControllerDelegate? weak var delegate: ComposeDrawingViewControllerDelegate?
@ -63,7 +87,8 @@ class ComposeDrawingViewController: UIViewController {
canvasView.drawingPolicy = .anyInput canvasView.drawingPolicy = .anyInput
canvasView.minimumZoomScale = 0.5 canvasView.minimumZoomScale = 0.5
canvasView.maximumZoomScale = 2 canvasView.maximumZoomScale = 2
canvasView.backgroundColor = .systemBackground canvasView.backgroundColor = .white
canvasView.overrideUserInterfaceStyle = .light
canvasView.translatesAutoresizingMaskIntoConstraints = false canvasView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(canvasView) view.addSubview(canvasView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -74,6 +99,7 @@ class ComposeDrawingViewController: UIViewController {
]) ])
toolPicker = PKToolPicker() toolPicker = PKToolPicker()
toolPicker.overrideUserInterfaceStyle = .light
toolPicker.setVisible(true, forFirstResponder: canvasView) toolPicker.setVisible(true, forFirstResponder: canvasView)
toolPicker.addObserver(canvasView) toolPicker.addObserver(canvasView)
toolPicker.addObserver(self) toolPicker.addObserver(self)

View File

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

View File

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

View File

@ -26,7 +26,7 @@ struct ComposeReplyView: View {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
HStack { HStack {
AccountDisplayNameLabel(account: status.account, fontSize: 17) AccountDisplayNameLabel(account: status.account, textStyle: .body, emojiSize: 17)
.lineLimit(1) .lineLimit(1)
.layoutPriority(1) .layoutPriority(1)
@ -51,6 +51,7 @@ struct ComposeReplyView: View {
.frame(height: contentHeight ?? 0) .frame(height: contentHeight ?? 0)
} }
} }
.frame(minHeight: 50, alignment: .top)
} }
private func replyAvatarImage(geometry: GeometryProxy) -> some View { private func replyAvatarImage(geometry: GeometryProxy) -> some View {
@ -63,7 +64,7 @@ struct ComposeReplyView: View {
offset = max(offset, 0) 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 // 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 // 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) offset = min(offset, maxOffset)

View File

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

View File

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

View File

@ -29,7 +29,11 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView) avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
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.textContainer.lineBreakMode = .byTruncatingTail
noteTextView.textContainerInset = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4) noteTextView.textContainerInset = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4)

View File

@ -9,78 +9,164 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
class TrendingStatusesViewController: EnhancedTableViewController { class TrendingStatusesViewController: UIViewController {
weak var mastodonController: MastodonController! 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) { init(mastodonController: MastodonController) {
self.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) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func viewDidLoad() { override func loadView() {
super.viewDidLoad() var config = UICollectionLayoutListConfiguration(appearance: .plain)
config.leadingSwipeActionsConfigurationProvider = { [unowned self] in
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.leadingSwipeActions()
}
config.trailingSwipeActionsConfigurationProvider = { [unowned self] in
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.trailingSwipeActions()
}
config.itemSeparatorHandler = { [unowned self] indexPath, sectionSeparatorConfiguration in
guard let item = self.dataSource.itemIdentifier(for: indexPath) else {
return sectionSeparatorConfiguration
}
var config = sectionSeparatorConfiguration
if item.hideSeparators {
config.topSeparatorVisibility = .hidden
config.bottomSeparatorVisibility = .hidden
}
if case .status(_, _) = item {
config.topSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
}
return config
}
let layout = UICollectionViewCompositionalLayout.list(using: config)
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dragDelegate = self
title = NSLocalizedString("Trending Posts", comment: "trending posts screen title") dataSource = createDataSource()
}
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell") private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
tableView.estimatedRowHeight = 144 let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in
dataSource = UITableViewDiffableDataSource(tableView: tableView, cellProvider: { tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
cell.delegate = self cell.delegate = self
cell.updateUI(statusID: item.id, state: item.state) cell.updateUI(statusID: item.0, state: item.1)
return cell }
}) 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) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
let request = Client.getTrendingStatuses() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses])
snapshot.appendItems([.loadingIndicator])
dataSource.apply(snapshot, animatingDifferences: false)
Task { Task {
guard let (statuses, _) = try? await mastodonController.run(request) else { await loadTrendingStatuses()
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)
}
} }
} }
// 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 { extension TrendingStatusesViewController {
enum Section { enum Section {
case statuses case statuses
} }
struct Item: Hashable { enum Item: Hashable {
let id: String case status(id: String, state: StatusState)
let state: StatusState case loadingIndicator
static func ==(lhs: Item, rhs: Item) -> Bool { var hideSeparators: Bool {
return lhs.id == rhs.id if case .loadingIndicator = self {
return true
} else {
return false
}
} }
func hash(into hasher: inout Hasher) { var isSelectable: Bool {
hasher.combine(id) 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 { extension TrendingStatusesViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController } var apiController: MastodonController! { mastodonController }
} }
@ -91,9 +177,19 @@ extension TrendingStatusesViewController: ToastableViewController {
extension TrendingStatusesViewController: MenuActionProvider { extension TrendingStatusesViewController: MenuActionProvider {
} }
extension TrendingStatusesViewController: StatusTableViewCellDelegate { extension TrendingStatusesViewController: StatusCollectionViewCellDelegate {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) {
tableView.beginUpdates() if let indexPath = collectionView.indexPath(for: cell) {
tableView.endUpdates() var snapshot = dataSource.snapshot()
snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
dataSource.apply(snapshot, animatingDifferences: animated, completion: completion)
}
}
}
extension TrendingStatusesViewController: StatusBarTappableViewController {
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
collectionView.scrollToTop()
return .stop
} }
} }

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -42,6 +42,7 @@
<constraint firstAttribute="height" constant="20" id="4tF-oL-qXT"/> <constraint firstAttribute="height" constant="20" id="4tF-oL-qXT"/>
<constraint firstAttribute="width" constant="20" id="zWx-jJ-dBj"/> <constraint firstAttribute="width" constant="20" id="zWx-jJ-dBj"/>
</constraints> </constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" image="square.and.arrow.up" catalog="system"/> <state key="normal" image="square.and.arrow.up" catalog="system"/>
<connections> <connections>
@ -54,7 +55,7 @@
<constraint firstAttribute="width" constant="20" id="eg0-hN-rda"/> <constraint firstAttribute="width" constant="20" id="eg0-hN-rda"/>
<constraint firstAttribute="height" constant="20" id="fmA-pI-8WB"/> <constraint firstAttribute="height" constant="20" id="fmA-pI-8WB"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="18"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<state key="normal" image="xmark" catalog="system"/> <state key="normal" image="xmark" catalog="system"/>
<connections> <connections>
@ -75,9 +76,9 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPa-Zu-T6g"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rPa-Zu-T6g">
<rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/> <rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/>
<subviews> <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"/> <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"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>

View File

@ -14,6 +14,7 @@ struct ComposingPrefsView: View {
var body: some View { var body: some View {
List { List {
visibilitySection
composingSection composingSection
replyingSection replyingSection
} }
@ -21,9 +22,9 @@ struct ComposingPrefsView: View {
.navigationBarTitle("Composing") .navigationBarTitle("Composing")
} }
var composingSection: some View { var visibilitySection: some View {
Section(header: Text("Composing")) { Section {
Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Post Visibility")) { Picker(selection: $preferences.defaultPostVisibility, label: Text("Default Visibility")) {
ForEach(Status.Visibility.allCases, id: \.self) { visibility in ForEach(Status.Visibility.allCases, id: \.self) { visibility in
HStack { HStack {
Image(systemName: visibility.imageName) Image(systemName: visibility.imageName)
@ -33,6 +34,26 @@ struct ComposingPrefsView: View {
}//.navigationBarTitle("Default Post Visibility") }//.navigationBarTitle("Default Post Visibility")
// navbar title on the ForEach is currently incorrectly applied when the picker is not expanded, see FB6838291 // 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) { Toggle(isOn: $preferences.automaticallySaveDrafts) {
Text("Automatically Save Drafts") Text("Automatically Save Drafts")
} }

View File

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

View File

@ -222,7 +222,17 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
} }
var snapshot = dataSource.snapshot() 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) snapshot.appendItems(items, toSection: .pinned)
await apply(snapshot, animatingDifferences: true) await apply(snapshot, animatingDifferences: true)
} }

View File

@ -185,21 +185,11 @@ extension MenuActionProvider {
favImage = favImage.withTintColor(UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1), renderingMode: .alwaysOriginal) 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 toggleableSection.insert(createAction(identifier: "favorite", title: favorited ? "Unfavorite" : "Favorite", image: favImage, handler: { [weak self] _ in
guard let self = self else { return } guard let self,
let request = (favorited ? Status.favourite : Status.unfavourite)(status.id) let navigationDelegate = self.navigationDelegate else { return }
self.mastodonController?.run(request, completion: { response in Task { @MainActor in
switch response { await FavoriteService(status: status, mastodonController: mastodonController, presenter: navigationDelegate).toggleFavorite()
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)
}
}
}
})
}), at: 0) }), at: 0)
let reblogged = status.reblogged 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) 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 toggleableSection.insert(createAction(identifier: "reblog", title: reblogged ? "Unreblog" : "Reblog", image: reblogImage, handler: { [weak self] _ in
guard let self = self else { return } guard let self,
let request: Request<Status> let navigationDelegate = self.navigationDelegate else { return }
if reblogged { Task { @MainActor in
request = Status.reblog(status.id) await ReblogService(status: status, mastodonController: mastodonController, presenter: navigationDelegate).toggleReblog()
} else {
request = Status.unreblog(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 \(reblogged ? "Unr" : "R")eblogging", in: toastable, retryAction: nil)
DispatchQueue.main.async {
toastable.showToast(configuration: config, animated: true)
}
}
}
})
}), at: 1) }), 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? let image: UIImage?
if let name = systemImageName { if let name = systemImageName {
image = UIImage(systemName: name) image = UIImage(systemName: name)

View File

@ -29,6 +29,12 @@ class AccountTableViewCell: UITableViewCell {
avatarImageView.layer.masksToBounds = true 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) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPrefrences), name: .preferencesChanged, object: nil)
} }

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="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"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -26,16 +28,16 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5">
<rect key="frame" x="74" y="11" width="230" height="78"/> <rect key="frame" x="74" y="11" width="230" height="78"/>
<subviews> <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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"> <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"/> <rect key="frame" x="0.0" y="20.5" width="230" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/> <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"/> <nil key="highlightedColor"/>
</label> </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"> <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"/> <point key="canvasLocation" x="173.91304347826087" y="35.491071428571423"/>
</tableViewCell> </tableViewCell>
</objects> </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> </document>

View File

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

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -23,9 +24,9 @@
<constraint firstAttribute="height" constant="64" id="ZhL-aE-TCF"/> <constraint firstAttribute="height" constant="64" id="ZhL-aE-TCF"/>
</constraints> </constraints>
</imageView> </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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -23,9 +24,9 @@
<constraint firstAttribute="width" constant="64" id="P7K-i4-Id7"/> <constraint firstAttribute="width" constant="64" id="P7K-i4-Id7"/>
</constraints> </constraints>
</imageView> </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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>

View File

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

View File

@ -86,28 +86,31 @@ extension BaseEmojiLabel {
func buildStringWithEmojisReplaced(usePlaceholders: Bool) -> NSAttributedString { func buildStringWithEmojisReplaced(usePlaceholders: Bool) -> NSAttributedString {
let mutAttrString = NSMutableAttributedString(attributedString: attributedString) 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) // 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) // 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 // so, just ignore the warnings
emojiImages.withLock { emojiImages in let emojiAttachments = emojiImages.withLock {
// replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis $0.mapValues { image in
for match in matches.reversed() { NSTextAttachment(emojiImage: image, in: self.emojiFont, with: self.emojiTextColor)
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 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 return mutAttrString
} }

View File

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

View File

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

View File

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

View File

@ -17,6 +17,13 @@ class DraftTableViewCell: UITableViewCell {
@IBOutlet weak var attachmentsStackViewContainer: UIView! @IBOutlet weak var attachmentsStackViewContainer: UIView!
@IBOutlet weak var attachmentsStackView: UIStackView! @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) { func updateUI(for draft: Draft) {
contentWarningLabel.text = draft.contentWarning contentWarningLabel.text = draft.contentWarning
contentWarningLabel.isHidden = !draft.contentWarningEnabled contentWarningLabel.isHidden = !draft.contentWarningEnabled

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <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"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -16,27 +18,27 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="gaD-3B-qO1"> <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> <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"> <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"/> <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"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zMS-88-DcM"> <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> <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"> <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="311.5" height="32"/> <rect key="frame" x="0.0" y="0.0" width="310.5" height="32"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"> <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="327.5" y="0.0" width="24.5" height="20.5"/> <rect key="frame" x="326.5" y="0.0" width="24.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<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"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@ -50,7 +52,7 @@
</constraints> </constraints>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="csc-gx-KVg"> <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> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="htC-hf-vJ4"> <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"/> <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"/> <point key="canvasLocation" x="-388" y="184.85757121439281"/>
</tableViewCell> </tableViewCell>
</objects> </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> </document>

View File

@ -28,8 +28,14 @@ class InstanceTableViewCell: UITableViewCell {
thumbnailImageView.layer.masksToBounds = true thumbnailImageView.layer.masksToBounds = true
thumbnailImageView.layer.cornerRadius = 5 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.masksToBounds = true
adultLabel.layer.cornerRadius = 0.5 * adultLabel.bounds.height adultLabel.layer.cornerRadius = 0.5 * adultLabel.bounds.height
descriptionTextView.defaultFont = .preferredFont(forTextStyle: .body)
descriptionTextView.adjustsFontForContentSizeCategory = true
} }
func updateUI(instance: InstanceSelector.Instance) { func updateUI(instance: InstanceSelector.Instance) {

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="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"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@ -27,34 +29,34 @@
</constraints> </constraints>
</imageView> </imageView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="QG1-xB-nmt"> <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> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="XtJ-BL-iHb"> <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"/> <rect key="frame" x="0.0" y="0.0" width="200" height="26.5"/>
<subviews> <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"/> <rect key="frame" x="0.0" y="0.0" width="164" height="26.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"/> <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> <constraints>
<constraint firstAttribute="height" constant="24" id="CNa-UL-LJh"/> <constraint firstAttribute="height" constant="24" id="CNa-UL-LJh"/>
<constraint firstAttribute="width" constant="36" id="tzn-KZ-r2f"/> <constraint firstAttribute="width" constant="36" id="tzn-KZ-r2f"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
</stackView> </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"> <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="36.5"/> <rect key="frame" x="0.0" y="26.5" width="200" height="38"/>
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/> <color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>
</subviews> </subviews>
@ -79,4 +81,12 @@
<point key="canvasLocation" x="131.8840579710145" y="178.79464285714286"/> <point key="canvasLocation" x="131.8840579710145" y="178.79464285714286"/>
</tableViewCell> </tableViewCell>
</objects> </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> </document>

View File

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

View File

@ -35,6 +35,13 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
override func awakeFromNib() { override func awakeFromNib() {
super.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 actionLabel.combiner = self.updateActionLabel
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -40,15 +41,15 @@
</label> </label>
</subviews> </subviews>
</stackView> </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"> <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="41"/> <rect key="frame" x="0.0" y="34" width="230" height="42.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"> <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="79" width="230" height="74"/> <rect key="frame" x="0.0" y="80.5" width="230" height="72.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>

View File

@ -31,6 +31,13 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
override func awakeFromNib() { override func awakeFromNib() {
super.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 actionLabel.combiner = self.updateActionLabel
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -40,9 +41,9 @@
</label> </label>
</subviews> </subviews>
</stackView> </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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@ -75,7 +76,7 @@
</tableViewCell> </tableViewCell>
</objects> </objects>
<resources> <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"> <systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor> </systemColor>

View File

@ -36,6 +36,13 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
override func awakeFromNib() { override func awakeFromNib() {
super.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 avatarImageView.layer.masksToBounds = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -41,9 +42,9 @@
</label> </label>
</subviews> </subviews>
</stackView> </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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@ -104,9 +105,9 @@
</tableViewCell> </tableViewCell>
</objects> </objects>
<resources> <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="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"> <systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor> </systemColor>

View File

@ -24,6 +24,20 @@ class PollFinishedTableViewCell: UITableViewCell {
private var updateTimestampWorkItem: DispatchWorkItem? 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) { func updateUI(notification: Pachyderm.Notification) {
guard let statusID = notification.status?.id, guard let statusID = notification.status?.id,
let status = delegate?.apiController.persistentContainer.status(for: statusID), let status = delegate?.apiController.persistentContainer.status(for: statusID),

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.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"> <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"/> <rect key="frame" x="0.0" y="0.0" width="232" height="20.5"/>
<subviews> <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"/> <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="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@ -43,9 +44,9 @@
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"/> <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"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@ -56,7 +57,7 @@
</subviews> </subviews>
</stackView> </stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark.square.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cqi-cV-ejs"> <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> <constraints>
<constraint firstAttribute="height" constant="30" id="E9e-iF-rqo"/> <constraint firstAttribute="height" constant="30" id="E9e-iF-rqo"/>
<constraint firstAttribute="width" constant="30" id="Efu-VP-pjH"/> <constraint firstAttribute="width" constant="30" id="Efu-VP-pjH"/>

View File

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

View File

@ -36,9 +36,7 @@ class ProfileHeaderView: UIView {
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var followsYouLabel: UILabel! @IBOutlet weak var followsYouLabel: UILabel!
@IBOutlet weak var noteTextView: StatusContentTextView! @IBOutlet weak var noteTextView: StatusContentTextView!
@IBOutlet weak var fieldsStackView: UIStackView! @IBOutlet weak var fieldsView: ProfileFieldsView!
@IBOutlet weak var fieldNamesStackView: UIStackView!
@IBOutlet weak var fieldValuesStackView: UIStackView!
@IBOutlet weak var pagesSegmentedControl: UISegmentedControl! @IBOutlet weak var pagesSegmentedControl: UISegmentedControl!
var accountID: String! var accountID: String!
@ -68,15 +66,31 @@ class ProfileHeaderView: UIView {
moreButton.layer.cornerRadius = 16 moreButton.layer.cornerRadius = 16
moreButton.layer.masksToBounds = true moreButton.layer.masksToBounds = true
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
moreButton.addInteraction(UIPointerInteraction(delegate: self)) moreButton.addInteraction(UIPointerInteraction(delegate: self))
moreButton.showsMenuAsPrimaryAction = true moreButton.showsMenuAsPrimaryAction = true
moreButton.isContextMenuInteractionEnabled = 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() { 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 = [] cancellables = []
mastodonController.persistentContainer.accountSubject mastodonController.persistentContainer.accountSubject
@ -128,43 +142,14 @@ class ProfileHeaderView: UIView {
} }
} }
fieldsStackView.isHidden = account.fields.isEmpty fieldsView.updateUI(account: account)
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)
}
accessibilityElements = [ accessibilityElements = [
displayNameLabel!, displayNameLabel!,
usernameLabel!, usernameLabel!,
noteTextView!, noteTextView!,
] + fieldAccessibilityElements + [ // TODO: voiceover for fieldsview
// fieldsView!,
moreButton!, moreButton!,
pagesSegmentedControl!, pagesSegmentedControl!,
] ]

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <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="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.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"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dgG-dR-lSv"> <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> <constraints>
<constraint firstAttribute="height" constant="150" id="aCE-CA-XWm"/> <constraint firstAttribute="height" constant="150" id="aCE-CA-XWm"/>
</constraints> </constraints>
</imageView> </imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wT9-2J-uSY"> <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> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TkY-oK-if4"> <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"/> <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"/> <constraint firstItem="TkY-oK-if4" firstAttribute="centerX" secondItem="wT9-2J-uSY" secondAttribute="centerX" id="ozz-sa-gSc"/>
</constraints> </constraints>
</view> </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"> <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="202" width="254" height="24"/> <rect key="frame" x="144" y="206" width="254" height="32"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRJ-Xf-kc9" customClass="VisualEffectImageButton" customModule="Tusker" customModuleProvider="target"> <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"/> <viewLayoutGuide key="safeArea" id="kQa-ou-pQz"/>
<accessibility key="accessibilityConfiguration" label="More Actions"> <accessibility key="accessibilityConfiguration" label="More Actions">
<accessibilityTraits key="traits" button="YES"/> <accessibilityTraits key="traits" button="YES"/>
@ -59,7 +60,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</view> </view>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="u4P-3i-gEq"> <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> <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"> <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"/> <rect key="frame" x="0.0" y="0.0" width="75.5" height="0.0"/>
@ -68,29 +69,21 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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"> <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> <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"/> <color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" distribution="fillProportionally" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="sp4-Zb-00B"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vKC-m1-Sbs" customClass="ProfileFieldsView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="194.5" width="382" height="358"/> <rect key="frame" x="0.0" y="267.5" width="398" height="128"/>
<subviews> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<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>
<constraints> <constraints>
<constraint firstItem="EfH-Dj-Jmn" firstAttribute="width" relation="greaterThanOrEqual" secondItem="aqG-FA-so5" secondAttribute="width" multiplier="0.5" id="2hZ-pF-C9b"/> <constraint firstAttribute="height" constant="128" placeholder="YES" id="xbR-M6-H0I"/>
<constraint firstItem="EfH-Dj-Jmn" firstAttribute="width" relation="lessThanOrEqual" secondItem="aqG-FA-so5" secondAttribute="width" multiplier="2" id="Lir-Ff-z0m"/>
</constraints> </constraints>
</stackView> </view>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="n1M-vM-Cj0"> <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> <segments>
<segment title="Posts"/> <segment title="Posts"/>
<segment title="Posts and Replies"/> <segment title="Posts and Replies"/>
@ -101,7 +94,7 @@
</connections> </connections>
</segmentedControl> </segmentedControl>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5ja-fK-Fqz"> <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"/> <color key="backgroundColor" systemColor="separatorColor"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="0.5" id="VwS-gV-q8M"/> <constraint firstAttribute="height" constant="0.5" id="VwS-gV-q8M"/>
@ -109,14 +102,14 @@
</view> </view>
</subviews> </subviews>
<constraints> <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="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="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"/> <constraint firstItem="1O8-2P-Gbf" firstAttribute="width" secondItem="u4P-3i-gEq" secondAttribute="width" constant="-16" id="hnA-3G-B9B"/>
</constraints> </constraints>
</stackView> </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"> <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"/> <fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <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="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="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="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="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="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="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="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="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="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 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 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> </constraints>
<connections> <connections>
<outlet property="avatarContainerView" destination="wT9-2J-uSY" id="yEm-h7-tfq"/> <outlet property="avatarContainerView" destination="wT9-2J-uSY" id="yEm-h7-tfq"/>
<outlet property="avatarImageView" destination="TkY-oK-if4" id="bSJ-7z-j4w"/> <outlet property="avatarImageView" destination="TkY-oK-if4" id="bSJ-7z-j4w"/>
<outlet property="displayNameLabel" destination="vcl-Gl-kXl" id="64n-a9-my0"/> <outlet property="displayNameLabel" destination="vcl-Gl-kXl" id="64n-a9-my0"/>
<outlet property="fieldNamesStackView" destination="aqG-FA-so5" id="prA-n7-blZ"/> <outlet property="fieldsView" destination="vKC-m1-Sbs" id="FeE-jh-lYH"/>
<outlet property="fieldValuesStackView" destination="EfH-Dj-Jmn" id="LMk-Hn-EkY"/>
<outlet property="fieldsStackView" destination="sp4-Zb-00B" id="eyx-GF-2Wf"/>
<outlet property="followsYouLabel" destination="UF8-nI-KVj" id="dTe-DQ-eJV"/> <outlet property="followsYouLabel" destination="UF8-nI-KVj" id="dTe-DQ-eJV"/>
<outlet property="headerImageView" destination="dgG-dR-lSv" id="HXT-v4-2iX"/> <outlet property="headerImageView" destination="dgG-dR-lSv" id="HXT-v4-2iX"/>
<outlet property="moreButton" destination="bRJ-Xf-kc9" id="zIN-pz-L7y"/> <outlet property="moreButton" destination="bRJ-Xf-kc9" id="zIN-pz-L7y"/>
@ -164,7 +156,7 @@
<resources> <resources>
<image name="ellipsis" catalog="system" width="128" height="37"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
<systemColor name="labelColor"> <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>
<systemColor name="secondaryLabelColor"> <systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>

View File

@ -182,7 +182,8 @@ class BaseStatusTableViewCell: UITableViewCell {
updateStatusIconsForPreferences(status) updateStatusIconsForPreferences(status)
if state.unknown { if state.unknown {
state.resolveFor(status: status, text: contentTextView.text) layoutIfNeeded()
state.resolveFor(status: status, height: contentTextView.bounds.height)
if state.collapsible! && showStatusAutomatically { if state.collapsible! && showStatusAutomatically {
state.collapsed = false state.collapsed = false
} }
@ -230,7 +231,17 @@ class BaseStatusTableViewCell: UITableViewCell {
func updateUIForPreferences(account: AccountMO, status: StatusMO) { func updateUIForPreferences(account: AccountMO, status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
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) updateStatusIconsForPreferences(status)

View File

@ -49,16 +49,35 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
moreButton!, 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] contentTextView.dataDetectorTypes = [.flightNumber, .address, .shipmentTrackingNumber, .phoneNumber]
if #available(iOS 16.0, *) { if #available(iOS 16.0, *) {
contentTextView.dataDetectorTypes.formUnion([.money, .physicalValue]) contentTextView.dataDetectorTypes.formUnion([.money, .physicalValue])
} }
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self)) let metaFont = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 15))
totalFavoritesButton.titleLabel!.font = metaFont
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly] totalFavoritesButton.titleLabel!.adjustsFontForContentSizeCategory = true
metaIndicatorsView.squeezeHorizontal = true totalReblogsButton.titleLabel!.font = metaFont
totalReblogsButton.titleLabel!.adjustsFontForContentSizeCategory = true
timestampAndClientLabel.font = metaFont
timestampAndClientLabel.adjustsFontForContentSizeCategory = true
} }
override func doUpdateUI(status: StatusMO, state: StatusState) { override func doUpdateUI(status: StatusMO, state: StatusState) {

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <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="Image references" minToolsVersion="12.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
@ -261,13 +261,13 @@
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/>
<image name="chevron.down" catalog="system" width="128" height="72"/> <image name="chevron.down" catalog="system" width="128" height="70"/>
<image name="ellipsis" catalog="system" width="128" height="37"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="repeat" catalog="system" width="128" height="98"/> <image name="repeat" catalog="system" width="128" height="98"/>
<image name="star.fill" catalog="system" width="128" height="116"/> <image name="star.fill" catalog="system" width="128" height="116"/>
<systemColor name="labelColor"> <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>
<systemColor name="opaqueSeparatorColor"> <systemColor name="opaqueSeparatorColor">
<color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

View File

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

View File

@ -104,7 +104,9 @@ extension StatusCollectionViewCell {
favoriteButton.isEnabled = mastodonController.loggedIn favoriteButton.isEnabled = mastodonController.loggedIn
if statusState.unknown { 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 { if statusState.collapsible! && showStatusAutomatically {
statusState.collapsed = false statusState.collapsed = false
} }
@ -145,7 +147,17 @@ extension StatusCollectionViewCell {
func baseUpdateUIForPreferences(status: StatusMO) { func baseUpdateUIForPreferences(status: StatusMO) {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.avatarImageViewSize avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * Self.avatarImageViewSize
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 let reblogButtonImage: UIImage
if Preferences.shared.alwaysShowStatusVisibilityIcon || reblogEnabled(status: status) { if Preferences.shared.alwaysShowStatusVisibilityIcon || reblogEnabled(status: status) {

View File

@ -11,7 +11,8 @@ import UIKit
class StatusContentContainer: UIView { class StatusContentContainer: UIView {
let contentTextView = StatusContentTextView().configure { 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.isScrollEnabled = false
$0.backgroundColor = nil $0.backgroundColor = nil
$0.isEditable = false $0.isEditable = false

View File

@ -13,11 +13,54 @@ class StatusMetaIndicatorsView: UIView {
var allowedIndicators: Indicator = .all var allowedIndicators: Indicator = .all
var squeezeHorizontal = false 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 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) { func updateUI(status: StatusMO) {
images.forEach { $0.removeFromSuperview() }
var images: [UIImage] = [] var images: [UIImage] = []
if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil { if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil {
@ -32,13 +75,66 @@ class StatusMetaIndicatorsView: UIView {
images.append(UIImage(named: "link.broken")!) images.append(UIImage(named: "link.broken")!)
} }
self.images = [] let views = images.map {
for (index, image) in images.enumerated() { let v = UIImageView(image: $0)
let v = UIImageView(image: image)
v.translatesAutoresizingMaskIntoConstraints = false v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFit v.contentMode = .scaleAspectFit
v.tintColor = .secondaryLabel 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) addSubview(v)
if index % 2 == 0 { 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.topAnchor.constraint(equalTo: self.images[index - 1].bottomAnchor, constant: 4).isActive = true
} }
v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
self.images.append(v)
} }
} }
}
extension StatusMetaIndicatorsView {
struct Indicator: OptionSet { struct Indicator: OptionSet {
let rawValue: Int let rawValue: Int
@ -81,4 +172,8 @@ extension StatusMetaIndicatorsView {
static let all: Indicator = [.reply, .visibility, .localOnly] static let all: Indicator = [.reply, .visibility, .localOnly]
} }
enum Alignment {
case leading, trailing
}
} }

View File

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

View File

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

View File

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