From cf71fc3f98f167cd87053fcfc00c1b0eaabd06fd Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Tue, 19 Mar 2024 15:11:47 -0400 Subject: [PATCH] Remove old gallery implementation See #450 --- Tusker.xcodeproj/project.pbxproj | 82 +--- .../Activities/GifvActivityItemSource.swift | 55 +++ .../Activities/ImageActivityItemSource.swift | 44 ++ .../UIViewController+Delegates.swift | 38 -- .../AttachmentPreviewViewController.swift | 50 --- .../GalleryPlayerViewController.swift | 70 --- .../GalleryViewController.swift | 179 -------- .../GifvAttachmentViewController.swift | 34 -- .../AttachmentViewController.swift | 0 ...FallbackGalleryContentViewController.swift | 0 .../GifvGalleryContentViewController.swift | 5 +- .../ImageGalleryContentViewController.swift | 2 + .../ImageGalleryDataSource.swift | 0 .../LoadingGalleryContentViewController.swift | 0 .../StatusAttachmentsGalleryDataSource.swift | 0 .../VideoGalleryContentViewController.swift | 7 +- .../GalleryFallbackViewController.swift | 73 ---- .../Large Image/LargeImageContentView.swift | 277 ------------ .../LargeImageViewController.swift | 397 ------------------ .../Large Image/LargeImageViewController.xib | 61 --- .../LoadingLargeImageViewController.swift | 176 -------- .../LargeImageExpandAnimationController.swift | 141 ------- .../LargeImageInteractionController.swift | 76 ---- .../LargeImageShrinkAnimationController.swift | 122 ------ .../Utilities/CustomAlertController.swift | 12 - Tusker/Screens/Utilities/Previewing.swift | 12 - Tusker/TuskerNavigationDelegate.swift | 2 +- Tusker/Views/Attachments/AttachmentView.swift | 22 +- 28 files changed, 133 insertions(+), 1804 deletions(-) create mode 100644 Tusker/Activities/GifvActivityItemSource.swift create mode 100644 Tusker/Activities/ImageActivityItemSource.swift delete mode 100644 Tusker/Extensions/UIViewController+Delegates.swift delete mode 100644 Tusker/Screens/Attachment Gallery/AttachmentPreviewViewController.swift delete mode 100644 Tusker/Screens/Attachment Gallery/GalleryPlayerViewController.swift delete mode 100644 Tusker/Screens/Attachment Gallery/GalleryViewController.swift delete mode 100644 Tusker/Screens/Attachment Gallery/GifvAttachmentViewController.swift rename Tusker/Screens/{Attachment Gallery => Gallery}/AttachmentViewController.swift (100%) rename Tusker/Screens/{Attachment Gallery => Gallery}/FallbackGalleryContentViewController.swift (100%) rename Tusker/Screens/{Attachment Gallery => Gallery}/GifvGalleryContentViewController.swift (92%) rename Tusker/Screens/{Attachment Gallery => Gallery}/ImageGalleryContentViewController.swift (97%) rename Tusker/Screens/{Attachment Gallery => Gallery}/ImageGalleryDataSource.swift (100%) rename Tusker/Screens/{Attachment Gallery => Gallery}/LoadingGalleryContentViewController.swift (100%) rename Tusker/Screens/{Attachment Gallery => Gallery}/StatusAttachmentsGalleryDataSource.swift (100%) rename Tusker/Screens/{Attachment Gallery => Gallery}/VideoGalleryContentViewController.swift (95%) delete mode 100644 Tusker/Screens/Large Image/GalleryFallbackViewController.swift delete mode 100644 Tusker/Screens/Large Image/LargeImageContentView.swift delete mode 100644 Tusker/Screens/Large Image/LargeImageViewController.swift delete mode 100644 Tusker/Screens/Large Image/LargeImageViewController.xib delete mode 100644 Tusker/Screens/Large Image/LoadingLargeImageViewController.swift delete mode 100644 Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift delete mode 100644 Tusker/Screens/Large Image/Transitions/LargeImageInteractionController.swift delete mode 100644 Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 4c61c826..dfbdc1cc 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -7,13 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 0411610022B442870030A9B7 /* LoadingLargeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041160FE22B442870030A9B7 /* LoadingLargeImageViewController.swift */; }; 0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */; }; 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0427033922B31269000D31B6 /* AdvancedPrefsView.swift */; }; 0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450531E22B0097E00100BA2 /* Timline+UI.swift */; }; 04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04586B4022B2FFB10021BD04 /* PreferencesView.swift */; }; 04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04586B4222B301470021BD04 /* AppearancePrefsView.swift */; }; - 04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D14BAE22B34A2800642648 /* GalleryViewController.swift */; }; 04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */; }; 04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* ImageCache.swift */; }; 04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; }; @@ -87,7 +85,6 @@ D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D627943123A5466600D38C68 /* SelectableTableViewCell.swift */; }; D627944D23A9A03D00D38C68 /* ListTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D627944C23A9A03D00D38C68 /* ListTimelineViewController.swift */; }; D627944F23A9C99800D38C68 /* EditListAccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D627944E23A9C99800D38C68 /* EditListAccountsViewController.swift */; }; - D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6289E83217B795D0003D1D7 /* LargeImageViewController.xib */; }; D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2421217AA7E1005076CC /* UserActivityManager.swift */; }; D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; }; D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; }; @@ -113,9 +110,6 @@ D6412B0D24B0D4CF00F5412E /* ProfileHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6412B0C24B0D4CF00F5412E /* ProfileHeaderView.swift */; }; D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; }; D642E83A2BA75F4C004BFD6A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D642E8392BA75F4C004BFD6A /* PrivacyInfo.xcprivacy */; }; - D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */; }; - D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */; }; - D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */; }; D646DCAC2A06C8840059ECEB /* ProfileFieldValueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCAB2A06C8840059ECEB /* ProfileFieldValueView.swift */; }; D646DCAE2A06C8C90059ECEB /* ProfileFieldVerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCAD2A06C8C90059ECEB /* ProfileFieldVerificationView.swift */; }; D646DCD22A06F2510059ECEB /* NotificationsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCD12A06F2510059ECEB /* NotificationsCollectionViewController.swift */; }; @@ -124,7 +118,6 @@ D646DCD82A07F3500059ECEB /* FollowRequestNotificationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCD72A07F3500059ECEB /* FollowRequestNotificationCollectionViewCell.swift */; }; D646DCDA2A081A2C0059ECEB /* PollFinishedNotificationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCD92A081A2C0059ECEB /* PollFinishedNotificationCollectionViewCell.swift */; }; D646DCDC2A081CF10059ECEB /* StatusUpdatedNotificationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646DCDB2A081CF10059ECEB /* StatusUpdatedNotificationCollectionViewCell.swift */; }; - D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */; }; D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9026C80DC600FC57FB /* ToastView.swift */; }; D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */; }; D64AAE9726C88DC400FC57FB /* ToastConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */; }; @@ -132,7 +125,6 @@ D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; }; D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */; }; D6531DEE246B81C9000F9538 /* GifvPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvPlayerView.swift */; }; - D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; }; D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */; }; D6552367289870790048A653 /* ScreenCorners in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, maccatalyst, ); productRef = D6552366289870790048A653 /* ScreenCorners */; }; D659F35E2953A212002D944A /* TTTKit in Frameworks */ = {isa = PBXBuildFile; productRef = D659F35D2953A212002D944A /* TTTKit */; }; @@ -154,7 +146,6 @@ D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; }; D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; }; D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; }; - D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; }; D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */; }; D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */; }; D66C900B28DAB7FD00217BF2 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */; }; @@ -166,7 +157,6 @@ D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */; }; D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */; }; D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68015412401A74600D6103B /* MediaPrefsView.swift */; }; - D681A29A249AD62D0085E54E /* LargeImageContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681A299249AD62D0085E54E /* LargeImageContentView.swift */; }; D681E4D7246E32290053414F /* StatusActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D6246E32290053414F /* StatusActivityItemSource.swift */; }; D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; }; D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; }; @@ -204,6 +194,8 @@ D6934F382BA8E2B7002B1C8D /* GifvController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F372BA8E2B7002B1C8D /* GifvController.swift */; }; D6934F3A2BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */; }; D6934F3C2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */; }; + D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */; }; + D6934F402BAA19EC002B1C8D /* GifvActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */; }; D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; }; D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; }; D693A73025CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */; }; @@ -287,10 +279,8 @@ D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */; }; D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C7D27C22B6EBF800071952 /* AttachmentsContainerView.swift */; }; D6C82B4125C5BB7E0017F1E6 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C82B4025C5BB7E0017F1E6 /* ExploreViewController.swift */; }; - D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D862139E62700CB5196 /* LargeImageViewController.swift */; }; D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D882139E6EC00CB5196 /* AttachmentView.swift */; }; D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */; }; - D6CA6A94249FADE700AD45C1 /* GalleryPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */; }; D6CA6ED229EF6091003EC5DF /* TuskerPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = D6CA6ED129EF6091003EC5DF /* TuskerPreferences */; }; D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */; }; D6CA8CDE296387310050C433 /* SaveToPhotosActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */; }; @@ -317,7 +307,6 @@ D6D79F532A0FFE3200AB2315 /* ToggleableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D79F522A0FFE3200AB2315 /* ToggleableButton.swift */; }; D6D79F572A1160B800AB2315 /* AccountDisplayNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */; }; D6D79F592A13293200AB2315 /* BackgroundManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D79F582A13293200AB2315 /* BackgroundManager.swift */; }; - D6D79F5B2A13D22B00AB2315 /* GalleryFallbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D79F5A2A13D22B00AB2315 /* GalleryFallbackViewController.swift */; }; D6D94955298963A900C59229 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D94954298963A900C59229 /* Colors.swift */; }; D6D9498D298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */; }; D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.swift */; }; @@ -418,13 +407,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 041160FE22B442870030A9B7 /* LoadingLargeImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingLargeImageViewController.swift; sourceTree = ""; }; 0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BehaviorPrefsView.swift; sourceTree = ""; }; 0427033922B31269000D31B6 /* AdvancedPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedPrefsView.swift; sourceTree = ""; }; 0450531E22B0097E00100BA2 /* Timline+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Timline+UI.swift"; sourceTree = ""; }; 04586B4022B2FFB10021BD04 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; 04586B4222B301470021BD04 /* AppearancePrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePrefsView.swift; sourceTree = ""; }; - 04D14BAE22B34A2800642648 /* GalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewController.swift; sourceTree = ""; }; 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarViewController.swift; sourceTree = ""; }; 04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; 04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = ""; }; @@ -497,7 +484,6 @@ D627943123A5466600D38C68 /* SelectableTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableTableViewCell.swift; sourceTree = ""; }; D627944C23A9A03D00D38C68 /* ListTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTimelineViewController.swift; sourceTree = ""; }; D627944E23A9C99800D38C68 /* EditListAccountsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditListAccountsViewController.swift; sourceTree = ""; }; - D6289E83217B795D0003D1D7 /* LargeImageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LargeImageViewController.xib; sourceTree = ""; }; D62D2421217AA7E1005076CC /* UserActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityManager.swift; sourceTree = ""; }; D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = ""; }; D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = ""; }; @@ -523,9 +509,6 @@ D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = ""; }; D642E8392BA75F4C004BFD6A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; D642E83D2BA7AD0F004BFD6A /* GalleryVC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = GalleryVC; sourceTree = ""; }; - D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageExpandAnimationController.swift; sourceTree = ""; }; - D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageShrinkAnimationController.swift; sourceTree = ""; }; - D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageInteractionController.swift; sourceTree = ""; }; D646DCAB2A06C8840059ECEB /* ProfileFieldValueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldValueView.swift; sourceTree = ""; }; D646DCAD2A06C8C90059ECEB /* ProfileFieldVerificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldVerificationView.swift; sourceTree = ""; }; D646DCD12A06F2510059ECEB /* NotificationsCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCollectionViewController.swift; sourceTree = ""; }; @@ -534,7 +517,6 @@ D646DCD72A07F3500059ECEB /* FollowRequestNotificationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowRequestNotificationCollectionViewCell.swift; sourceTree = ""; }; D646DCD92A081A2C0059ECEB /* PollFinishedNotificationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFinishedNotificationCollectionViewCell.swift; sourceTree = ""; }; D646DCDB2A081CF10059ECEB /* StatusUpdatedNotificationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusUpdatedNotificationCollectionViewCell.swift; sourceTree = ""; }; - D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentPreviewViewController.swift; sourceTree = ""; }; D64AAE9026C80DC600FC57FB /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastableViewController.swift; sourceTree = ""; }; D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfiguration.swift; sourceTree = ""; }; @@ -542,7 +524,6 @@ D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = ""; }; D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldsView.swift; sourceTree = ""; }; D6531DED246B81C9000F9538 /* GifvPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvPlayerView.swift; sourceTree = ""; }; - D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = ""; }; D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSwipeActionProvider.swift; sourceTree = ""; }; D659F36129541065002D944A /* TTTView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTTView.swift; sourceTree = ""; }; D65B4B532971F71D00DABDFB /* EditedReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditedReport.swift; sourceTree = ""; }; @@ -565,7 +546,6 @@ D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusStateResolver.swift; sourceTree = ""; }; D663626B21361C6700C9CBA2 /* Account+Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Preferences.swift"; sourceTree = ""; }; D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcutItems.swift; sourceTree = ""; }; - D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Delegates.swift"; sourceTree = ""; }; D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Equatable.swift"; sourceTree = ""; }; D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Traits.swift"; sourceTree = ""; }; D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = ""; }; @@ -578,7 +558,6 @@ D67C57AE21E28EAD00C3118B /* Array+Uniques.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Uniques.swift"; sourceTree = ""; }; D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposingPrefsView.swift; sourceTree = ""; }; D68015412401A74600D6103B /* MediaPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPrefsView.swift; sourceTree = ""; }; - D681A299249AD62D0085E54E /* LargeImageContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageContentView.swift; sourceTree = ""; }; D681E4D6246E32290053414F /* StatusActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActivityItemSource.swift; sourceTree = ""; }; D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = ""; }; D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = ""; }; @@ -616,6 +595,8 @@ D6934F372BA8E2B7002B1C8D /* GifvController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvController.swift; sourceTree = ""; }; D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FallbackGalleryContentViewController.swift; sourceTree = ""; }; D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoGalleryContentViewController.swift; sourceTree = ""; }; + D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageActivityItemSource.swift; sourceTree = ""; }; + D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvActivityItemSource.swift; sourceTree = ""; }; D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = ""; }; D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = ""; }; D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeaturedProfileCollectionViewCell.xib; sourceTree = ""; }; @@ -699,10 +680,8 @@ D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = ""; }; D6C7D27C22B6EBF800071952 /* AttachmentsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsContainerView.swift; sourceTree = ""; }; D6C82B4025C5BB7E0017F1E6 /* ExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreViewController.swift; sourceTree = ""; }; - D6C94D862139E62700CB5196 /* LargeImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageViewController.swift; sourceTree = ""; }; D6C94D882139E6EC00CB5196 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = ""; }; D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSessionHelper.swift; sourceTree = ""; }; - D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryPlayerViewController.swift; sourceTree = ""; }; D6CA6ED029EF6060003EC5DF /* TuskerPreferences */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = TuskerPreferences; sourceTree = ""; }; D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUniqueTests.swift; sourceTree = ""; }; D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveToPhotosActivity.swift; sourceTree = ""; }; @@ -736,7 +715,6 @@ D6D79F522A0FFE3200AB2315 /* ToggleableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleableButton.swift; sourceTree = ""; }; D6D79F562A1160B800AB2315 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = ""; }; D6D79F582A13293200AB2315 /* BackgroundManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundManager.swift; sourceTree = ""; }; - D6D79F5A2A13D22B00AB2315 /* GalleryFallbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryFallbackViewController.swift; sourceTree = ""; }; D6D94954298963A900C59229 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; D6D9498C298CBB4000C59229 /* TrendingStatusCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingStatusCollectionViewCell.swift; sourceTree = ""; }; D6D9498E298EB79400C59229 /* CopyableLable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLable.swift; sourceTree = ""; }; @@ -842,13 +820,9 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0411610522B457290030A9B7 /* Attachment Gallery */ = { + 0411610522B457290030A9B7 /* Gallery */ = { isa = PBXGroup; children = ( - 04D14BAE22B34A2800642648 /* GalleryViewController.swift */, - D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */, - D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */, - D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */, D6934F2D2BA7AEF9002B1C8D /* StatusAttachmentsGalleryDataSource.swift */, D6934F332BA8D65A002B1C8D /* ImageGalleryDataSource.swift */, D6934F312BA7E43E002B1C8D /* LoadingGalleryContentViewController.swift */, @@ -857,7 +831,7 @@ D6934F3B2BAA0F80002B1C8D /* VideoGalleryContentViewController.swift */, D6934F392BA8F3D7002B1C8D /* FallbackGalleryContentViewController.swift */, ); - path = "Attachment Gallery"; + path = Gallery; sourceTree = ""; }; D60089172981FEA4005B4D00 /* Tip Jar */ = { @@ -1017,14 +991,13 @@ children = ( D65B4B89297879DE00DABDFB /* Account Follows */, D6A3BC822321F69400FD64D5 /* Account List */, - 0411610522B457290030A9B7 /* Attachment Gallery */, D641C787213DD862004B4513 /* Compose */, D641C785213DD83B004B4513 /* Conversation */, D6F2E960249E772F005846BB /* Crash Reporter */, D61F759729384D4200C0B37F /* Customize Timelines */, D627943C23A5635D00D38C68 /* Explore */, D6A4DCC92553666600D9DE31 /* Fast Account Switcher */, - D641C788213DD86D004B4513 /* Large Image */, + 0411610522B457290030A9B7 /* Gallery */, D627944B23A9A02400D38C68 /* Lists */, D627944823A6AD5100D38C68 /* Local Predicate Statuses List */, D641C782213DD7F0004B4513 /* Main */, @@ -1127,19 +1100,6 @@ path = Compose; sourceTree = ""; }; - D641C788213DD86D004B4513 /* Large Image */ = { - isa = PBXGroup; - children = ( - D646C954213B364600269FB5 /* Transitions */, - D6289E83217B795D0003D1D7 /* LargeImageViewController.xib */, - D6C94D862139E62700CB5196 /* LargeImageViewController.swift */, - D681A299249AD62D0085E54E /* LargeImageContentView.swift */, - 041160FE22B442870030A9B7 /* LoadingLargeImageViewController.swift */, - D6D79F5A2A13D22B00AB2315 /* GalleryFallbackViewController.swift */, - ); - path = "Large Image"; - sourceTree = ""; - }; D641C789213DD87E004B4513 /* Preferences */ = { isa = PBXGroup; children = ( @@ -1208,16 +1168,6 @@ path = Packages; sourceTree = ""; }; - D646C954213B364600269FB5 /* Transitions */ = { - isa = PBXGroup; - children = ( - D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */, - D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */, - D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */, - ); - path = Transitions; - sourceTree = ""; - }; D64AAE8F26C80DB600FC57FB /* Toast */ = { isa = PBXGroup; children = ( @@ -1272,7 +1222,6 @@ D667E5F62135C2ED0057A976 /* Extensions */ = { isa = PBXGroup; children = ( - D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */, D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */, D663626B21361C6700C9CBA2 /* Account+Preferences.swift */, D6F4D79329ECB0AF00351B87 /* UIBackgroundConfiguration+AppColors.swift */, @@ -1398,6 +1347,8 @@ children = ( D681E4D6246E32290053414F /* StatusActivityItemSource.swift */, D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */, + D6934F3D2BAA19D5002B1C8D /* ImageActivityItemSource.swift */, + D6934F3F2BAA19EC002B1C8D /* GifvActivityItemSource.swift */, D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */, D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */, D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */, @@ -1902,7 +1853,6 @@ D6E57FA325C26FAB00341037 /* Localizable.stringsdict in Resources */, D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */, D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */, - D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */, D601FA84297EEC3F00A8E8B5 /* SuggestedProfileCardCollectionViewCell.xib in Resources */, D6F2E966249E8BFD005846BB /* IssueReporterViewController.xib in Resources */, D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */, @@ -1996,7 +1946,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */, 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */, D6C3F4F5298ED0890009FCFF /* LocalPredicateStatusesViewController.swift in Sources */, D691771729A710520054D7EF /* ProfileNoContentCollectionViewCell.swift in Sources */, @@ -2036,7 +1985,6 @@ D6B22A0F2560D52D004D82EF /* TabbedPageViewController.swift in Sources */, D6895DC228D65274006341DA /* CustomAlertController.swift in Sources */, D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */, - 0411610022B442870030A9B7 /* LoadingLargeImageViewController.swift in Sources */, D6A4DCE525537C7A00D9DE31 /* FastSwitchingAccountView.swift in Sources */, D6A4DCCC2553667800D9DE31 /* FastAccountSwitcherViewController.swift in Sources */, D627944F23A9C99800D38C68 /* EditListAccountsViewController.swift in Sources */, @@ -2055,7 +2003,6 @@ D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */, D646DCD82A07F3500059ECEB /* FollowRequestNotificationCollectionViewCell.swift in Sources */, D6F2E965249E8BFD005846BB /* IssueReporterViewController.swift in Sources */, - D6CA6A94249FADE700AD45C1 /* GalleryPlayerViewController.swift in Sources */, D627944D23A9A03D00D38C68 /* ListTimelineViewController.swift in Sources */, D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */, D6D94955298963A900C59229 /* Colors.swift in Sources */, @@ -2079,10 +2026,8 @@ D6A6C10F25B62D2400298D0F /* DiskCache.swift in Sources */, D6C3F4F9298EDBF20009FCFF /* ConversationTree.swift in Sources */, D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */, - D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */, D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */, D6DD8FFF2984D327002AD3FD /* BookmarksViewController.swift in Sources */, - D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */, D646DCD22A06F2510059ECEB /* NotificationsCollectionViewController.swift in Sources */, D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */, D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameView.swift in Sources */, @@ -2162,7 +2107,6 @@ D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */, D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */, D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */, - D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */, D6D79F592A13293200AB2315 /* BackgroundManager.swift in Sources */, D6E4269D2532A3E100C02E1C /* FuzzyMatcher.swift in Sources */, D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */, @@ -2190,7 +2134,6 @@ D646DCAC2A06C8840059ECEB /* ProfileFieldValueView.swift in Sources */, D6F4D79429ECB0AF00351B87 /* UIBackgroundConfiguration+AppColors.swift in Sources */, D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */, - D681A29A249AD62D0085E54E /* LargeImageContentView.swift in Sources */, D6C041C42AED77730094D32D /* EditListSettingsService.swift in Sources */, D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */, D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */, @@ -2210,6 +2153,7 @@ D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */, D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */, D6934F302BA7AF91002B1C8D /* ImageGalleryContentViewController.swift in Sources */, + D6934F3E2BAA19D5002B1C8D /* ImageActivityItemSource.swift in Sources */, D67C1795266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift in Sources */, D61F75962937037800C0B37F /* ToggleFollowHashtagService.swift in Sources */, D60089192981FEBA005B4D00 /* ConfettiView.swift in Sources */, @@ -2221,7 +2165,6 @@ D6E77D0B286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift in Sources */, D6114E1127F899B30080E273 /* TrendingLinksViewController.swift in Sources */, D65B4B58297203A700DABDFB /* ReportSelectRulesView.swift in Sources */, - D6D79F5B2A13D22B00AB2315 /* GalleryFallbackViewController.swift in Sources */, D6C3F4FB299035650009FCFF /* TrendsViewController.swift in Sources */, D6B81F442560390300F6E31D /* MenuController.swift in Sources */, D691771129A2B76A0054D7EF /* MainActor+Unsafe.swift in Sources */, @@ -2231,14 +2174,12 @@ D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */, D68A76F129539116001DA1B3 /* FlipView.swift in Sources */, D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */, - D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */, D61F75B5293BD97400C0B37F /* DeleteFilterService.swift in Sources */, D6DFC6A0242C4CCC00ACC392 /* Weak.swift in Sources */, D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */, D61ABEF828EFC3F900B29151 /* ProfileStatusesViewController.swift in Sources */, D6ADB6EA28E91C30009924AB /* TimelineStatusCollectionViewCell.swift in Sources */, D681E4D7246E32290053414F /* StatusActivityItemSource.swift in Sources */, - D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */, D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */, D621733328F1D5ED004C7DB1 /* ReblogService.swift in Sources */, D62275A824F1CA2800B82A16 /* ComposeReplyContentView.swift in Sources */, @@ -2254,7 +2195,6 @@ D6895DC428D65342006341DA /* ConfirmReblogStatusPreviewView.swift in Sources */, D62E9987279D094F00C26176 /* StatusMetaIndicatorsView.swift in Sources */, D6BEA247291A0F2D002F4D01 /* Duckable+Root.swift in Sources */, - 04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */, D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */, D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */, D6F6A554291F0D9600F496A8 /* DeleteListService.swift in Sources */, @@ -2267,11 +2207,11 @@ D68015422401A74600D6103B /* MediaPrefsView.swift in Sources */, D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */, D6E77D09286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift in Sources */, - D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */, D6D79F292A0D596B00AB2315 /* StatusEditHistoryViewController.swift in Sources */, D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */, D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */, D61F75B3293BD89C00C0B37F /* UpdateFilterService.swift in Sources */, + D6934F402BAA19EC002B1C8D /* GifvActivityItemSource.swift in Sources */, D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */, D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */, D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */, diff --git a/Tusker/Activities/GifvActivityItemSource.swift b/Tusker/Activities/GifvActivityItemSource.swift new file mode 100644 index 00000000..9520aacc --- /dev/null +++ b/Tusker/Activities/GifvActivityItemSource.swift @@ -0,0 +1,55 @@ +// +// GifvActivityItemSource.swift +// Tusker +// +// Created by Shadowfacts on 3/19/24. +// Copyright © 2024 Shadowfacts. All rights reserved. +// + +import UIKit +import UniformTypeIdentifiers +import AVFoundation + +class GifvActivityItemSource: NSObject, UIActivityItemSource { + let asset: AVAsset + let url: URL + + init(asset: AVAsset, url: URL) { + self.asset = asset + self.url = url + } + + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return url + } + + func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { + #if os(visionOS) + #warning("Use async AVAssetImageGenerator.image(at:)") + return nil + #else + let generator = AVAssetImageGenerator(asset: self.asset) + generator.appliesPreferredTrackTransform = true + if let image = try? generator.copyCGImage(at: .zero, actualTime: nil) { + return UIImage(cgImage: image) + } else { + return nil + } + #endif + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + do { + let data = try Data(contentsOf: url) + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent) + try data.write(to: tempURL) + return tempURL + } catch { + return nil + } + } + + func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String { + return (UTType(filenameExtension: url.pathExtension) ?? .video).identifier + } +} diff --git a/Tusker/Activities/ImageActivityItemSource.swift b/Tusker/Activities/ImageActivityItemSource.swift new file mode 100644 index 00000000..c02a8aee --- /dev/null +++ b/Tusker/Activities/ImageActivityItemSource.swift @@ -0,0 +1,44 @@ +// +// ImageActivityItemSource.swift +// Tusker +// +// Created by Shadowfacts on 3/19/24. +// Copyright © 2024 Shadowfacts. All rights reserved. +// + +import UIKit +import UniformTypeIdentifiers + +class ImageActivityItemSource: NSObject, UIActivityItemSource { + let data: Data + let url: URL + let image: UIImage? + + init(data: Data, url: URL, image: UIImage?) { + self.data = data + self.url = url + self.image = image + } + + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return url + } + + func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { + return image + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + do { + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent) + try data.write(to: tempURL) + return tempURL + } catch { + return nil + } + } + + func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String { + return (UTType(filenameExtension: url.pathExtension) ?? .image).identifier + } +} diff --git a/Tusker/Extensions/UIViewController+Delegates.swift b/Tusker/Extensions/UIViewController+Delegates.swift deleted file mode 100644 index 03e8e24a..00000000 --- a/Tusker/Extensions/UIViewController+Delegates.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// UIViewController+Delegate.swift -// Tusker -// -// Created by Shadowfacts on 8/27/18. -// Copyright © 2018 Shadowfacts. All rights reserved. -// - -#if !os(visionOS) -import UIKit - -extension UIViewController: UIViewControllerTransitioningDelegate { - public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - if let presented = presented as? LargeImageAnimatableViewController, - presented.animationImage != nil { - return LargeImageExpandAnimationController() - } - return nil - } - - public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - if let dismissed = dismissed as? LargeImageAnimatableViewController, - dismissed.animationImage != nil { - return LargeImageShrinkAnimationController(interactionController: dismissed.dismissInteractionController) - } - return nil - } - - public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { - if let animator = animator as? LargeImageShrinkAnimationController, - let interactionController = animator.interactionController, - interactionController.inProgress { - return interactionController - } - return nil - } -} -#endif diff --git a/Tusker/Screens/Attachment Gallery/AttachmentPreviewViewController.swift b/Tusker/Screens/Attachment Gallery/AttachmentPreviewViewController.swift deleted file mode 100644 index 92e5f504..00000000 --- a/Tusker/Screens/Attachment Gallery/AttachmentPreviewViewController.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// AttachmentPreviewViewController.swift -// Tusker -// -// Created by Shadowfacts on 3/20/20. -// Copyright © 2020 Shadowfacts. All rights reserved. -// - -import UIKit -import Pachyderm -import TuskerComponents - -class AttachmentPreviewViewController: UIViewController { - - private let attachment: Attachment - private let sourceView: AttachmentView - - init(sourceView: AttachmentView) { - self.attachment = sourceView.attachment - self.sourceView = sourceView - - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func loadView() { - if let data = ImageCache.attachments.getData(attachment.url), - let image = UIImage(data: data) { - let imageView: UIImageView - if attachment.url.pathExtension == "gif" { - let gifView = GIFImageView(image: image) - let controller = sourceView.gifController ?? GIFController(gifData: data) - controller.attach(to: gifView) - controller.startAnimating() - imageView = gifView - } else { - imageView = UIImageView(image: image) - } - imageView.contentMode = .scaleAspectFit - imageView.backgroundColor = .black - view = imageView - preferredContentSize = image.size - } else { - view = UIActivityIndicatorView(style: .large) - } - } -} diff --git a/Tusker/Screens/Attachment Gallery/GalleryPlayerViewController.swift b/Tusker/Screens/Attachment Gallery/GalleryPlayerViewController.swift deleted file mode 100644 index 2d3f265d..00000000 --- a/Tusker/Screens/Attachment Gallery/GalleryPlayerViewController.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// GalleryPlayerViewController.swift -// Tusker -// -// Created by Shadowfacts on 6/21/20. -// Copyright © 2020 Shadowfacts. All rights reserved. -// - -import UIKit -import AVKit -import Pachyderm - -class GalleryPlayerViewController: UIViewController { - - let playerVC = AVPlayerViewController() - - var attachment: Attachment! - - private var isFirstAppearance = true - - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .black - - playerVC.allowsPictureInPicturePlayback = true - - playerVC.view.translatesAutoresizingMaskIntoConstraints = false - addChild(playerVC) - playerVC.didMove(toParent: self) - view.addSubview(playerVC.view) - - NSLayoutConstraint.activate([ - playerVC.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - playerVC.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - playerVC.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - playerVC.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), - ]) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - // starting while audio is already playing from another app often takes nearly a second, - // so do it on a background thread as to not block the UI - DispatchQueue.global(qos: .userInitiated).async { - AudioSessionHelper.enable() - AudioSessionHelper.setVideoPlayback() - - DispatchQueue.main.async { - if self.isFirstAppearance { - self.isFirstAppearance = false - self.playerVC.player?.play() - } - } - } - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - - // starting often takes around half a second, - // so do it on a background thread as to not block the UI - DispatchQueue.global(qos: .userInitiated).async { - AudioSessionHelper.setDefault() - AudioSessionHelper.disable() - } - } - -} diff --git a/Tusker/Screens/Attachment Gallery/GalleryViewController.swift b/Tusker/Screens/Attachment Gallery/GalleryViewController.swift deleted file mode 100644 index ee35fc11..00000000 --- a/Tusker/Screens/Attachment Gallery/GalleryViewController.swift +++ /dev/null @@ -1,179 +0,0 @@ -// GalleryViewController.swift -// Tusker -// -// Created by Shadowfacts on 6/13/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import UIKit -import Pachyderm -import AVFoundation -import AVKit - -class GalleryViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, LargeImageAnimatableViewController { - - weak var avPlayerViewControllerDelegate: AVPlayerViewControllerDelegate? - - let attachments: [Attachment] - let sourceViews: WeakArray - let startIndex: Int - - var pages: [UIViewController]! - - var currentIndex: Int { - guard let vc = viewControllers?.first, - let index = pages.firstIndex(of: vc) else { - fatalError() - } - return index - } - - var animationSourceView: UIImageView? { sourceViews[currentIndex] } - var largeImageController: LargeImageViewController? { - // use protocol because page controllers may be loading or non-loading VCs - (pages[currentIndex] as? LargeImageAnimatableViewController)?.largeImageController - } - var animationImage: UIImage? { - if let page = pages[currentIndex] as? LargeImageAnimatableViewController, - let image = page.animationImage { - return image - } else { - return animationSourceView?.image - } - } - var dismissInteractionController: LargeImageInteractionController? - - var isInteractivelyAnimatingDismissal: Bool = false { - didSet { - #if !os(visionOS) - setNeedsStatusBarAppearanceUpdate() - #endif - } - } - - override var prefersStatusBarHidden: Bool { - return !isInteractivelyAnimatingDismissal - } - override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { - return .none - } - override var childForHomeIndicatorAutoHidden: UIViewController? { - return viewControllers?.first - } - - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - if UIDevice.current.userInterfaceIdiom == .phone { - return .allButUpsideDown - } else { - return .all - } - } - - init(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) { - self.attachments = attachments - self.sourceViews = WeakArray(sourceViews) - self.startIndex = startIndex - - super.init(transitionStyle: .scroll, navigationOrientation: .horizontal) - - modalPresentationStyle = .fullScreen - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - self.pages = attachments.enumerated().map { (index, attachment) in - switch attachment.kind { - case .image: - let vc = LoadingLargeImageViewController(attachment: attachment) - vc.shrinkGestureEnabled = false - vc.animationSourceView = sourceViews[index] - return vc - case .video, .audio: - let vc = GalleryPlayerViewController() - vc.playerVC.player = AVPlayer(url: attachment.url) - vc.playerVC.delegate = avPlayerViewControllerDelegate - vc.attachment = attachment - return vc - case .gifv: - // Passing the source view to the LargeImageGifvContentView is a crappy workaround for not - // having the current frame to use as the animationImage. This will break when there - // are more than 4 attachments and there is a gifv at index >= 3 (the More... button will show - // in place of the fourth attachment, so there aren't source views for the attachments at index >= 3). - // This isn't a priority as only Mastodon converts gifs to gifvs, and Mastodon (in its default configuration, - // I don't know about forks) doesn't allow more than four attachments, meaning there will always be a source view. - let gifvContentView = LargeImageGifvContentView(attachment: attachment, source: sourceViews[index]!) - let vc = LargeImageViewController(contentView: gifvContentView, description: attachment.description, sourceView: nil) - vc.shrinkGestureEnabled = false - return vc - default: - return UINavigationController(rootViewController: GalleryFallbackViewController(attachment: attachment)) - } - } - - setViewControllers([pages[startIndex]], direction: .forward, animated: false) - - self.dataSource = self - self.delegate = self - - overrideUserInterfaceStyle = .dark - - dismissInteractionController = LargeImageInteractionController(viewController: self) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if let vc = pages[currentIndex] as? AVPlayerViewController { - // when the gallery is first shown, after the transition finishes, the controls for the player controller appear semi-transparent - // hiding the controls and then immediately reshowing them makes sure they're visible when the gallery is presented - vc.showsPlaybackControls = false - vc.showsPlaybackControls = true - - // begin playing the video as soon as we appear - vc.player?.play() - } - } - - override func accessibilityPerformEscape() -> Bool { - dismiss(animated: true) - return true - } - - // MARK: - Page View Controller Data Source - - func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { - guard let index = pages.firstIndex(of: viewController), - index > 0 else { - return nil - } - return pages[index - 1] - } - - func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { - guard let index = pages.firstIndex(of: viewController), - index < pages.count - 1 else { - return nil - } - return pages[index + 1] - } - - // MARK: - Page View Controller Delegate - func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { - if let pending = pendingViewControllers.first as? LoadingLargeImageViewController, - let current = viewControllers!.first as? LoadingLargeImageViewController { - pending.controlsVisible = current.controlsVisible - } - - if let pending = pendingViewControllers.first as? AVPlayerViewController { - // show controls and begin playing when the player page becomes visible - pending.showsPlaybackControls = true - pending.player?.play() - } - } - -} diff --git a/Tusker/Screens/Attachment Gallery/GifvAttachmentViewController.swift b/Tusker/Screens/Attachment Gallery/GifvAttachmentViewController.swift deleted file mode 100644 index 107c6899..00000000 --- a/Tusker/Screens/Attachment Gallery/GifvAttachmentViewController.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// GifvAttachmentViewController.swift -// Tusker -// -// Created by Shadowfacts on 5/12/20. -// Copyright © 2020 Shadowfacts. All rights reserved. -// - -import UIKit -import Pachyderm -import AVFoundation - -class GifvAttachmentViewController: UIViewController { - - private let attachment: Attachment - - init(attachment: Attachment) { - precondition(attachment.kind == .gifv) - self.attachment = attachment - - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func loadView() { - let asset = AVURLAsset(url: attachment.url) - let controller = GifvController(asset: asset) - self.view = GifvPlayerView(controller: controller, gravity: .resizeAspect) - } - -} diff --git a/Tusker/Screens/Attachment Gallery/AttachmentViewController.swift b/Tusker/Screens/Gallery/AttachmentViewController.swift similarity index 100% rename from Tusker/Screens/Attachment Gallery/AttachmentViewController.swift rename to Tusker/Screens/Gallery/AttachmentViewController.swift diff --git a/Tusker/Screens/Attachment Gallery/FallbackGalleryContentViewController.swift b/Tusker/Screens/Gallery/FallbackGalleryContentViewController.swift similarity index 100% rename from Tusker/Screens/Attachment Gallery/FallbackGalleryContentViewController.swift rename to Tusker/Screens/Gallery/FallbackGalleryContentViewController.swift diff --git a/Tusker/Screens/Attachment Gallery/GifvGalleryContentViewController.swift b/Tusker/Screens/Gallery/GifvGalleryContentViewController.swift similarity index 92% rename from Tusker/Screens/Attachment Gallery/GifvGalleryContentViewController.swift rename to Tusker/Screens/Gallery/GifvGalleryContentViewController.swift index 2075e90a..b8a11cd4 100644 --- a/Tusker/Screens/Attachment Gallery/GifvGalleryContentViewController.swift +++ b/Tusker/Screens/Gallery/GifvGalleryContentViewController.swift @@ -41,9 +41,12 @@ class GifvGalleryContentViewController: UIViewController, GalleryContentViewCont ]) presentationSizeCancellable = controller.presentationSizeSubject - .sink { [unowned self] _ in + .sink { [unowned self] size in + self.preferredContentSize = size self.container?.galleryContentChanged() } + + preferredContentSize = controller.item.presentationSize } override func viewWillAppear(_ animated: Bool) { diff --git a/Tusker/Screens/Attachment Gallery/ImageGalleryContentViewController.swift b/Tusker/Screens/Gallery/ImageGalleryContentViewController.swift similarity index 97% rename from Tusker/Screens/Attachment Gallery/ImageGalleryContentViewController.swift rename to Tusker/Screens/Gallery/ImageGalleryContentViewController.swift index e5a7ea8c..5e62ec76 100644 --- a/Tusker/Screens/Attachment Gallery/ImageGalleryContentViewController.swift +++ b/Tusker/Screens/Gallery/ImageGalleryContentViewController.swift @@ -26,6 +26,8 @@ class ImageGalleryContentViewController: UIViewController, GalleryContentViewCon self.gifController = gifController super.init(nibName: nil, bundle: nil) + + preferredContentSize = image.size } required init?(coder: NSCoder) { diff --git a/Tusker/Screens/Attachment Gallery/ImageGalleryDataSource.swift b/Tusker/Screens/Gallery/ImageGalleryDataSource.swift similarity index 100% rename from Tusker/Screens/Attachment Gallery/ImageGalleryDataSource.swift rename to Tusker/Screens/Gallery/ImageGalleryDataSource.swift diff --git a/Tusker/Screens/Attachment Gallery/LoadingGalleryContentViewController.swift b/Tusker/Screens/Gallery/LoadingGalleryContentViewController.swift similarity index 100% rename from Tusker/Screens/Attachment Gallery/LoadingGalleryContentViewController.swift rename to Tusker/Screens/Gallery/LoadingGalleryContentViewController.swift diff --git a/Tusker/Screens/Attachment Gallery/StatusAttachmentsGalleryDataSource.swift b/Tusker/Screens/Gallery/StatusAttachmentsGalleryDataSource.swift similarity index 100% rename from Tusker/Screens/Attachment Gallery/StatusAttachmentsGalleryDataSource.swift rename to Tusker/Screens/Gallery/StatusAttachmentsGalleryDataSource.swift diff --git a/Tusker/Screens/Attachment Gallery/VideoGalleryContentViewController.swift b/Tusker/Screens/Gallery/VideoGalleryContentViewController.swift similarity index 95% rename from Tusker/Screens/Attachment Gallery/VideoGalleryContentViewController.swift rename to Tusker/Screens/Gallery/VideoGalleryContentViewController.swift index 6a0882e5..2aee0c74 100644 --- a/Tusker/Screens/Attachment Gallery/VideoGalleryContentViewController.swift +++ b/Tusker/Screens/Gallery/VideoGalleryContentViewController.swift @@ -14,7 +14,7 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon private let url: URL let caption: String? private let item: AVPlayerItem - private let player: AVPlayer + let player: AVPlayer private var presentationSizeObservation: NSKeyValueObservation? private var statusObservation: NSKeyValueObservation? @@ -50,8 +50,9 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon playerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) - presentationSizeObservation = item.observe(\.presentationSize, changeHandler: { [unowned self] _, _ in + presentationSizeObservation = item.observe(\.presentationSize, changeHandler: { [unowned self] item, _ in MainActor.runUnsafely { + self.preferredContentSize = item.presentationSize self.container?.galleryContentChanged() } }) @@ -63,6 +64,8 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon } } }) + + preferredContentSize = item.presentationSize } override func viewWillAppear(_ animated: Bool) { diff --git a/Tusker/Screens/Large Image/GalleryFallbackViewController.swift b/Tusker/Screens/Large Image/GalleryFallbackViewController.swift deleted file mode 100644 index acadb35c..00000000 --- a/Tusker/Screens/Large Image/GalleryFallbackViewController.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// GalleryFallbackViewController.swift -// Tusker -// -// Created by Shadowfacts on 5/16/23. -// Copyright © 2023 Shadowfacts. All rights reserved. -// - -import UIKit -import QuickLook -import Pachyderm - -class GalleryFallbackViewController: QLPreviewController { - - private let attachment: Attachment - private let previewItem: GalleryPreviewItem - - private var loadAttachmentTask: Task? - - deinit { - loadAttachmentTask?.cancel() - } - - init(attachment: Attachment) { - self.attachment = attachment - self.previewItem = GalleryPreviewItem() - - super.init(nibName: nil, bundle: nil) - - dataSource = self - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if previewItem.previewItemURL == nil, - loadAttachmentTask == nil { - loadAttachmentTask = Task { - do { - let (data, _) = try await URLSession.shared.data(from: attachment.url) - let url = FileManager.default.temporaryDirectory.appendingPathComponent(attachment.url.lastPathComponent) - try data.write(to: url) - try Task.checkCancellation() - previewItem.previewItemURL = url - refreshCurrentPreviewItem() - } catch { - return - } - } - } - } - -} - -extension GalleryFallbackViewController: QLPreviewControllerDataSource { - nonisolated func numberOfPreviewItems(in controller: QLPreviewController) -> Int { - return 1 - } - - nonisolated func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { - return MainActor.runUnsafely { - previewItem - } - } -} - -private class GalleryPreviewItem: NSObject, QLPreviewItem { - var previewItemURL: URL? = nil -} diff --git a/Tusker/Screens/Large Image/LargeImageContentView.swift b/Tusker/Screens/Large Image/LargeImageContentView.swift deleted file mode 100644 index a3e2c8f5..00000000 --- a/Tusker/Screens/Large Image/LargeImageContentView.swift +++ /dev/null @@ -1,277 +0,0 @@ -// -// LargeImageContentView.swift -// Tusker -// -// Created by Shadowfacts on 6/17/20. -// Copyright © 2020 Shadowfacts. All rights reserved. -// - -import UIKit -import Pachyderm -@preconcurrency import AVFoundation -@preconcurrency import VisionKit -import TuskerComponents - -@MainActor -protocol LargeImageContentView: UIView { - var animationImage: UIImage? { get } - var activityItemsForSharing: [Any] { get } - var owner: LargeImageViewController? { get set } - func setControlsVisible(_ controlsVisible: Bool, animated: Bool) - func grayscaleStateChanged() -} - -class LargeImageImageContentView: UIImageView, LargeImageContentView { - - #if !targetEnvironment(macCatalyst) - @available(iOS 16.0, *) - private static let analyzer = ImageAnalyzer() - private var _analysisInteraction: AnyObject? - @available(iOS 16.0, *) - private var analysisInteraction: ImageAnalysisInteraction? { _analysisInteraction as? ImageAnalysisInteraction } - #endif - - var animationImage: UIImage? { image! } - var activityItemsForSharing: [Any] { - guard let data else { - return [] - } - return [ImageActivityItemSource(data: data, url: url, image: image)] - } - weak var owner: LargeImageViewController? - - private let url: URL - private let data: Data? - - init(url: URL, data: Data?, image: UIImage) { - self.url = url - self.data = data - - super.init(image: image) - - contentMode = .scaleAspectFit - isUserInteractionEnabled = true - - #if !targetEnvironment(macCatalyst) - if #available(iOS 16.0, *), - ImageAnalyzer.isSupported { - let interaction = ImageAnalysisInteraction() - self._analysisInteraction = interaction - interaction.delegate = self - interaction.preferredInteractionTypes = .automatic - addInteraction(interaction) - Task { - do { - let result = try await LargeImageImageContentView.analyzer.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode])) - interaction.analysis = result - } catch { - // if analysis fails, we just don't show anything - } - } - } - #endif - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setControlsVisible(_ controlsVisible: Bool, animated: Bool) { - #if !targetEnvironment(macCatalyst) - if #available(iOS 16.0, *), - let analysisInteraction { - analysisInteraction.setSupplementaryInterfaceHidden(!controlsVisible, animated: animated) - } - #endif - } - - func grayscaleStateChanged() { - guard let data else { - return - } - - let image: UIImage? - if Preferences.shared.grayscaleImages { - image = ImageGrayscalifier.convert(url: nil, data: data) - } else { - image = UIImage(data: data) - } - - if let image = image { - self.image = image - } - } -} - -#if !targetEnvironment(macCatalyst) -@available(iOS 16.0, *) -extension LargeImageImageContentView: ImageAnalysisInteractionDelegate { - func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? { - return owner - } -} -#endif - -class LargeImageGifContentView: GIFImageView, LargeImageContentView { - var animationImage: UIImage? { image } - var activityItemsForSharing: [Any] { - [ImageActivityItemSource(data: gifController!.gifData, url: url, image: image)] - } - weak var owner: LargeImageViewController? - - private let url: URL - - init(url: URL, gifController: GIFController) { - self.url = url - - super.init(image: gifController.lastFrame?.image) - - contentMode = .scaleAspectFit - - gifController.attach(to: self) - // todo: doing this in the init feels wrong - gifController.startAnimating() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setControlsVisible(_ controlsVisible: Bool, animated: Bool) { - } - - func grayscaleStateChanged() { - // todo - } -} - -class LargeImageGifvContentView: GifvPlayerView, LargeImageContentView { - private(set) var animationImage: UIImage? - var activityItemsForSharing: [Any] { - [GifvActivityItemSource(asset: asset, attachment: attachment)] - } - weak var owner: LargeImageViewController? - - private let attachment: Attachment - private let asset: AVURLAsset - - private var videoSize: CGSize? - override var intrinsicContentSize: CGSize { - videoSize ?? CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric) - } - - init(attachment: Attachment, source: UIImageView) { - precondition(attachment.kind == .gifv) - - self.attachment = attachment - self.asset = AVURLAsset(url: attachment.url) - - let controller = GifvController(asset: asset) - super.init(controller: controller, gravity: .resizeAspect) - - self.animationImage = source.image - - controller.play() - - Task { - do { - if let track = try await asset.loadTracks(withMediaType: .video).first { - let (size, transform) = try await track.load(.naturalSize, .preferredTransform) - self.videoSize = size.applying(transform) - self.invalidateIntrinsicContentSize() - } - } catch { - } - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setControlsVisible(_ controlsVisible: Bool, animated: Bool) { - } - - func grayscaleStateChanged() { - // no-op, GifvPlayerView observes the grayscale state itself - } - -} - -class ImageActivityItemSource: NSObject, UIActivityItemSource { - let data: Data - let url: URL - let image: UIImage? - - init(data: Data, url: URL, image: UIImage?) { - self.data = data - self.url = url - self.image = image - } - - func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { - return url - } - - func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { - return image - } - - func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { - do { - let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent) - try data.write(to: tempURL) - return tempURL - } catch { - return nil - } - } - - func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String { - return (UTType(filenameExtension: url.pathExtension) ?? .image).identifier - } -} - -class GifvActivityItemSource: NSObject, UIActivityItemSource { - let asset: AVAsset - let attachment: Attachment - - init(asset: AVAsset, attachment: Attachment) { - self.asset = asset - self.attachment = attachment - } - - func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { - return attachment.url - } - - func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { - #if os(visionOS) - #warning("Use async AVAssetImageGenerator.image(at:)") - return nil - #else - let generator = AVAssetImageGenerator(asset: self.asset) - generator.appliesPreferredTrackTransform = true - if let image = try? generator.copyCGImage(at: .zero, actualTime: nil) { - return UIImage(cgImage: image) - } else { - return nil - } - #endif - } - - func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { - do { - let data = try Data(contentsOf: attachment.url) - let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(attachment.url.lastPathComponent) - try data.write(to: tempURL) - return tempURL - } catch { - return nil - } - } - - func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String { - return (UTType(filenameExtension: attachment.url.pathExtension) ?? .video).identifier - } -} diff --git a/Tusker/Screens/Large Image/LargeImageViewController.swift b/Tusker/Screens/Large Image/LargeImageViewController.swift deleted file mode 100644 index c0dfe930..00000000 --- a/Tusker/Screens/Large Image/LargeImageViewController.swift +++ /dev/null @@ -1,397 +0,0 @@ -// -// LargeImageViewController.swift -// Tusker -// -// Created by Shadowfacts on 8/31/18. -// Copyright © 2018 Shadowfacts. All rights reserved. -// - -import UIKit - -class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeImageAnimatableViewController { - - weak var animationSourceView: UIImageView? - var largeImageController: LargeImageViewController? { self } - var animationImage: UIImage? { contentView.animationImage } - var dismissInteractionController: LargeImageInteractionController? - - @IBOutlet weak var scrollView: UIScrollView! - @IBOutlet weak var topControlsView: UIView! - @IBOutlet weak var descriptionTextView: UITextView! - - private var shareContainer: UIView! - private var closeContainer: UIView! - private var shareImage: UIImageView! - private var shareButtonTopConstraint: NSLayoutConstraint! - private var shareButtonLeadingConstraint: NSLayoutConstraint! - private var closeButtonTopConstraint: NSLayoutConstraint! - private var closeButtonTrailingConstraint: NSLayoutConstraint! - - var contentView: LargeImageContentView { - didSet { - oldValue.removeFromSuperview() - setupContentView() - } - } - var contentViewLeadingConstraint: NSLayoutConstraint! - var contentViewTopConstraint: NSLayoutConstraint! - - var imageDescription: String? - - var initialControlsVisible = true - private(set) var controlsVisible = true { - didSet { - setNeedsUpdateOfHomeIndicatorAutoHidden() - } - } - var shrinkGestureEnabled = true - - private var isInitialAppearance = true - private var skipUpdatingControlsWhileZooming = false - private var prevZoomScale: CGFloat? - private var isGrayscale = false - private var contentViewSizeObservation: NSKeyValueObservation? - - var isInteractivelyAnimatingDismissal: Bool = false { - didSet { - #if !os(visionOS) - setNeedsStatusBarAppearanceUpdate() - #endif - } - } - - override var prefersStatusBarHidden: Bool { - return !isInteractivelyAnimatingDismissal - } - override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { - return .none - } - - override var prefersHomeIndicatorAutoHidden: Bool { - return !controlsVisible - } - - init(contentView: LargeImageContentView, description: String?, sourceView: UIImageView?) { - self.imageDescription = description - self.animationSourceView = sourceView - - self.contentView = contentView - - super.init(nibName: "LargeImageViewController", bundle: nil) - - modalPresentationStyle = .fullScreen - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - setupContentView() - setupControls() - - setControlsVisible(initialControlsVisible, animated: false) - if contentView.activityItemsForSharing.isEmpty { - shareContainer.isUserInteractionEnabled = false - shareImage.tintColor = .systemGray - } - - scrollView.delegate = self - - if let imageDescription = imageDescription, - !imageDescription.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - descriptionTextView.text = imageDescription.trimmingCharacters(in: .whitespacesAndNewlines) - descriptionTextView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) - // i'm not sure why .automatic doesn't work for this - descriptionTextView.contentInsetAdjustmentBehavior = .always - let height = min(150, descriptionTextView.contentSize.height) - descriptionTextView.topAnchor.constraint(equalTo: descriptionTextView.safeAreaLayoutGuide.bottomAnchor, constant: -(height + 16)).isActive = true - } else { - descriptionTextView.isHidden = true - } - - if shrinkGestureEnabled { - dismissInteractionController = LargeImageInteractionController(viewController: self) - } - - let singleTap = UITapGestureRecognizer(target: self, action: #selector(scrollViewPressed(_:))) - let doubleTap = UITapGestureRecognizer(target: self, action: #selector(scrollViewDoubleTapped(_:))) - doubleTap.numberOfTapsRequired = 2 - // this requirement is needed to make sure the double tap is ever recognized - singleTap.require(toFail: doubleTap) - view.addGestureRecognizer(singleTap) - view.addGestureRecognizer(doubleTap) - - NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) - - accessibilityElements = [ - topControlsView!, - contentView, - descriptionTextView!, - ] - } - - private func setupContentView() { - contentView.owner = self - contentView.translatesAutoresizingMaskIntoConstraints = false - scrollView.addSubview(contentView) - contentViewLeadingConstraint = contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor) - contentViewTopConstraint = contentView.topAnchor.constraint(equalTo: scrollView.topAnchor) - NSLayoutConstraint.activate([ - contentViewLeadingConstraint, - contentViewTopConstraint, - ]) - contentViewSizeObservation = (contentView as UIView).observe(\.bounds, changeHandler: { [unowned self] _, _ in - MainActor.runUnsafely { - self.centerImage() - } - }) - } - - private func setupControls() { - shareContainer = UIView() - shareContainer.isAccessibilityElement = true - shareContainer.accessibilityTraits = .button - shareContainer.accessibilityLabel = "Share" - shareContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(sharePressed))) - shareContainer.translatesAutoresizingMaskIntoConstraints = false - topControlsView.addSubview(shareContainer) - shareImage = UIImageView(image: UIImage(systemName: "square.and.arrow.up")) - shareImage.tintColor = .white - shareImage.contentMode = .scaleAspectFit - shareImage.translatesAutoresizingMaskIntoConstraints = false - shareContainer.addSubview(shareImage) - shareButtonTopConstraint = shareImage.topAnchor.constraint(greaterThanOrEqualTo: shareContainer.topAnchor) - shareButtonLeadingConstraint = shareImage.leadingAnchor.constraint(greaterThanOrEqualTo: shareContainer.leadingAnchor) - NSLayoutConstraint.activate([ - shareContainer.topAnchor.constraint(equalTo: topControlsView.topAnchor), - shareContainer.leadingAnchor.constraint(equalTo: topControlsView.leadingAnchor), - shareContainer.bottomAnchor.constraint(equalTo: topControlsView.bottomAnchor), - shareContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: 50), - shareContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: 50), - - shareImage.centerXAnchor.constraint(equalTo: shareContainer.centerXAnchor), - shareImage.centerYAnchor.constraint(equalTo: shareContainer.centerYAnchor), - shareButtonTopConstraint, - shareButtonLeadingConstraint, - - shareImage.widthAnchor.constraint(equalToConstant: 24), - shareImage.heightAnchor.constraint(equalToConstant: 24), - ]) - - closeContainer = UIView() - closeContainer.isAccessibilityElement = true - closeContainer.accessibilityTraits = .button - closeContainer.accessibilityLabel = "Close" - closeContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(closeButtonPressed))) - closeContainer.translatesAutoresizingMaskIntoConstraints = false - topControlsView.addSubview(closeContainer) - let closeImage = UIImageView(image: UIImage(systemName: "xmark")) - closeImage.tintColor = .white - closeImage.contentMode = .scaleAspectFit - closeImage.translatesAutoresizingMaskIntoConstraints = false - closeContainer.addSubview(closeImage) - closeButtonTopConstraint = closeImage.topAnchor.constraint(greaterThanOrEqualTo: closeContainer.topAnchor) - closeButtonTrailingConstraint = closeContainer.trailingAnchor.constraint(greaterThanOrEqualTo: closeImage.trailingAnchor) - NSLayoutConstraint.activate([ - closeContainer.topAnchor.constraint(equalTo: topControlsView.topAnchor), - closeContainer.trailingAnchor.constraint(equalTo: topControlsView.trailingAnchor), - closeContainer.bottomAnchor.constraint(equalTo: closeContainer.bottomAnchor), - closeContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: 50), - closeContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: 50), - - closeImage.centerXAnchor.constraint(equalTo: closeContainer.centerXAnchor), - closeImage.centerYAnchor.constraint(equalTo: closeContainer.centerYAnchor), - closeButtonTopConstraint, - closeButtonTrailingConstraint, - - closeImage.widthAnchor.constraint(equalToConstant: 24), - closeImage.heightAnchor.constraint(equalToConstant: 24), - ]) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - - // limit the image height to the safe area height, so the image doesn't overlap the top controls - // while zoomed all the way out - let maxHeight = view.bounds.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom - let heightScale = maxHeight / contentView.intrinsicContentSize.height - let widthScale = view.bounds.width / contentView.intrinsicContentSize.width - let minScale = min(widthScale, heightScale) - skipUpdatingControlsWhileZooming = true - scrollView.minimumZoomScale = minScale - scrollView.zoomScale = minScale - scrollView.maximumZoomScale = minScale >= 1 ? minScale + 2 : 2 - skipUpdatingControlsWhileZooming = false - - centerImage() - - let notchedDeviceTopInsets: [CGFloat] = [ - 44, // iPhone X, Xs, Xs Max, 11 Pro, 11 Pro Max - 48, // iPhone XR, 11 - 47, // iPhone 12, 12 Pro, 12 Pro Max, 13, 13 Pro, 13 Pro Max, 14, 14 Plus - 50, // iPhone 12 mini, 13 mini - ] - let pillDeviceTopInsets: [CGFloat] = [ - 59, // iPhone 14 Pro, 14 Pro Max - ] - if notchedDeviceTopInsets.contains(view.safeAreaInsets.top) { - // the notch width is not the same for the iPhones 13, - // but what we actually want is the same offset from the edges - // since the corner radius didn't change - let notchWidth: CGFloat = 210 - let earWidth = (view.bounds.width - notchWidth) / 2 - let offset = (earWidth - shareImage.bounds.width) / 2 - shareButtonLeadingConstraint.constant = offset - closeButtonTrailingConstraint.constant = offset - } else if pillDeviceTopInsets.contains(view.safeAreaInsets.top) { - shareButtonLeadingConstraint.constant = 24 - shareButtonTopConstraint.constant = 24 - closeButtonTrailingConstraint.constant = 24 - closeButtonTopConstraint.constant = 24 - } - } - - override func viewSafeAreaInsetsDidChange() { - super.viewSafeAreaInsetsDidChange() - // the controls view transforms take the safe area insets into account, so they need to be updated - updateControlsView() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - // on the first appearance, the text view flashes its own scroll indicators automatically - // so we only need to do it on subsequent appearances - if isInitialAppearance { - isInitialAppearance = false - } else { - if animated && controlsVisible && !descriptionTextView.isHidden { - descriptionTextView.flashScrollIndicators() - } - } - } - - @objc private func preferencesChanged() { - if isGrayscale != Preferences.shared.grayscaleImages { - isGrayscale = Preferences.shared.grayscaleImages - contentView.grayscaleStateChanged() - } - } - - func setControlsVisible(_ controlsVisible: Bool, animated: Bool) { - self.controlsVisible = controlsVisible - if animated { - UIView.animate(withDuration: 0.2) { - // note: the value of animated: is passed to ImageAnalysisInteractino.setSupplementaryInterfaceHidden which (as of iOS 17.0.2 (21A350)): - // - does not animate with animated:false when wrapped in a UIView.animate block - // - does not animate with animated:true unless also wrapped in a UIView.animate block - self.contentView.setControlsVisible(controlsVisible, animated: true) - self.updateControlsView() - } - if controlsVisible && !descriptionTextView.isHidden { - descriptionTextView.flashScrollIndicators() - } - } else { - contentView.setControlsVisible(controlsVisible, animated: false) - updateControlsView() - } - } - - func updateControlsView() { - let topOffset = self.controlsVisible ? 0 : -(self.topControlsView.bounds.height + self.view.safeAreaInsets.top) - self.topControlsView.transform = CGAffineTransform(translationX: 0, y: topOffset) - if self.imageDescription != nil { - let bottomOffset = self.controlsVisible ? 0 : self.descriptionTextView.bounds.height + self.view.safeAreaInsets.bottom - self.descriptionTextView.transform = CGAffineTransform(translationX: 0, y: bottomOffset) - } - } - - func viewForZooming(in scrollView: UIScrollView) -> UIView? { - return contentView - } - - func scrollViewDidZoom(_ scrollView: UIScrollView) { - let prevZoomScale = self.prevZoomScale ?? scrollView.minimumZoomScale - if !skipUpdatingControlsWhileZooming { - if scrollView.zoomScale <= scrollView.minimumZoomScale { - setControlsVisible(true, animated: true) - } else if scrollView.zoomScale > prevZoomScale { - setControlsVisible(false, animated: true) - } - } - self.prevZoomScale = scrollView.zoomScale - } - - func centerImage() { - let yOffset = max(0, (view.bounds.size.height - contentView.frame.height) / 2) - contentViewTopConstraint.constant = yOffset - - let xOffset = max(0, (view.bounds.size.width - contentView.frame.width) / 2) - contentViewLeadingConstraint.constant = xOffset - } - - func zoomRectFor(scale: CGFloat, center: CGPoint) -> CGRect { - var zoomRect = CGRect.zero - zoomRect.size.width = contentView.frame.width / scale - zoomRect.size.height = contentView.frame.height / scale - let newCenter = scrollView.convert(center, to: contentView) - zoomRect.origin.x = newCenter.x - (zoomRect.width / 2) - zoomRect.origin.y = newCenter.y - (zoomRect.height / 2) - return zoomRect - } - - // MARK: Interaction - - func animateZoomOut() { - UIView.animate(withDuration: 0.3, animations: { - self.scrollView.zoomScale = self.scrollView.minimumZoomScale - self.view.layoutIfNeeded() - }) - } - - @objc func scrollViewPressed(_ sender: UITapGestureRecognizer) { - if scrollView.zoomScale > scrollView.minimumZoomScale { - animateZoomOut() - } else { - setControlsVisible(!controlsVisible, animated: true) - } - } - - @objc func scrollViewDoubleTapped(_ recognizer: UITapGestureRecognizer) { - if scrollView.zoomScale <= scrollView.minimumZoomScale { - let point = recognizer.location(in: recognizer.view) - let scale: CGFloat - if scrollView.minimumZoomScale < 1 { - if 1 - scrollView.zoomScale <= 0.5 { - scale = scrollView.zoomScale + 1 - } else { - scale = 1 - } - } else { - scale = scrollView.maximumZoomScale - } - let rect = zoomRectFor(scale: scale, center: point) - UIView.animate(withDuration: 0.3) { - self.scrollView.zoom(to: rect, animated: false) - self.view.layoutIfNeeded() - } - } else { - animateZoomOut() - } - } - - @IBAction func closeButtonPressed(_ sender: Any) { - dismiss(animated: true) - } - - @IBAction func sharePressed(_ sender: Any) { - let activityVC = UIActivityViewController(activityItems: contentView.activityItemsForSharing, applicationActivities: [SaveToPhotosActivity()]) - activityVC.popoverPresentationController?.sourceView = shareImage - present(activityVC, animated: true) - } - -} diff --git a/Tusker/Screens/Large Image/LargeImageViewController.xib b/Tusker/Screens/Large Image/LargeImageViewController.xib deleted file mode 100644 index b11e50d8..00000000 --- a/Tusker/Screens/Large Image/LargeImageViewController.xib +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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. - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift b/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift deleted file mode 100644 index 4df24731..00000000 --- a/Tusker/Screens/Large Image/LoadingLargeImageViewController.swift +++ /dev/null @@ -1,176 +0,0 @@ -// LoadingLargeImageViewController.swift -// Tusker -// -// Created by Shadowfacts on 6/14/19. -// Copyright © 2019 Shadowfacts. All rights reserved. -// - -import UIKit -import Pachyderm -import TuskerComponents - -class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableViewController { - - private var attachment: Attachment? - let url: URL - let cache: ImageCache - let imageDescription: String? - - private(set) var loaded = false - private(set) var largeImageVC: LargeImageViewController? - private var loadingVC: LoadingViewController? - - private var imageRequest: ImageCache.Request? - - private var initialControlsVisible: Bool = true - var controlsVisible: Bool { - get { - return largeImageVC?.controlsVisible ?? initialControlsVisible - } - set { - if let largeImageVC = largeImageVC { - largeImageVC.setControlsVisible(newValue, animated: false) - } else { - initialControlsVisible = newValue - } - } - } - - var shrinkGestureEnabled = true - - weak var animationSourceView: UIImageView? - var largeImageController: LargeImageViewController? { largeImageVC } - var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image } - var dismissInteractionController: LargeImageInteractionController? - - var isInteractivelyAnimatingDismissal: Bool = false { - didSet { - #if !os(visionOS) - setNeedsStatusBarAppearanceUpdate() - #endif - } - } - - override var prefersStatusBarHidden: Bool { - return !isInteractivelyAnimatingDismissal - } - override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { - return .none - } - override var childForHomeIndicatorAutoHidden: UIViewController? { - return largeImageVC - } - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - if UIDevice.current.userInterfaceIdiom == .phone { - return .allButUpsideDown - } else { - return .all - } - } - - init(url: URL, cache: ImageCache, imageDescription: String?) { - self.url = url - self.cache = cache - self.imageDescription = imageDescription - - super.init(nibName: nil, bundle: nil) - - modalPresentationStyle = .fullScreen - } - - convenience init(attachment: Attachment) { - self.init(url: attachment.url, cache: .attachments, imageDescription: attachment.description) - self.attachment = attachment - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - overrideUserInterfaceStyle = .dark - view.backgroundColor = .black - - // always load full resolution from disk for large image, in case the cache is scaled - if let entry = cache.get(url, loadOriginal: true) { - // todo: if load original is true, is there any way entry.data could be nil? - // feels like the data param of createLargeImage shouldn't be optional - createLargeImage(data: entry.data, image: entry.image, url: url) - } else { - createPreview() - - loadingVC = LoadingViewController() - embedChild(loadingVC!) - imageRequest = cache.get(url, loadOriginal: true) { [weak self] (data, image) in - guard let self = self, let image = image else { return } - DispatchQueue.main.async { - self.imageRequest = nil - self.loadingVC?.removeViewAndController() - self.createLargeImage(data: data, image: image, url: self.url) - } - } - } - - if shrinkGestureEnabled { - dismissInteractionController = LargeImageInteractionController(viewController: self) - } - } - - override func didMove(toParent parent: UIViewController?) { - super.didMove(toParent: parent) - - if parent == nil { - imageRequest?.cancel() - } - } - - private func createLargeImage(data: Data?, image: UIImage, url: URL) { - guard !loaded else { return } - loaded = true - - let content: LargeImageContentView - - // todo: p sure grayscaling gifs has never worked - if url.pathExtension == "gif", let data = data { - // todo: pulling the gif controller out of the source view feels icky - // is it possible for the source view's gif controller to have different data than we just got? - // should this be a property set by the animation controller instead? - let gifController = (animationSourceView as? GIFImageView)?.gifController ?? GIFController(gifData: data) - content = LargeImageGifContentView(url: url, gifController: gifController) - } else { - if let transformedImage = ImageGrayscalifier.convertIfNecessary(url: url, image: image) { - content = LargeImageImageContentView(url: url, data: data, image: transformedImage) - } else { - content = LargeImageImageContentView(url: url, data: data, image: image) - } - } - - setContent(content) - } - - private func setContent(_ content: LargeImageContentView) { - if let existing = largeImageVC { - existing.contentView = content - } else { - largeImageVC = LargeImageViewController(contentView: content, description: imageDescription, sourceView: animationSourceView) - largeImageVC!.initialControlsVisible = initialControlsVisible - largeImageVC!.shrinkGestureEnabled = false - embedChild(largeImageVC!) - } - } - - private func createPreview() { - guard !self.loaded, - var image = animationSourceView?.image else { return } - - if Preferences.shared.grayscaleImages, - let source = image.cgImage, - let grayscale = ImageGrayscalifier.convert(url: nil, cgImage: source) { - image = grayscale - } - setContent(LargeImageImageContentView(url: url, data: nil, image: image)) - } - -} diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift deleted file mode 100644 index ac257539..00000000 --- a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift +++ /dev/null @@ -1,141 +0,0 @@ -// -// LargeImageExpandAnimationController.swift -// Tusker -// -// Created by Shadowfacts on 9/1/18. -// Copyright © 2018 Shadowfacts. All rights reserved. -// - -import UIKit -import TuskerComponents - -@MainActor -protocol LargeImageAnimatableViewController: UIViewController { - var animationSourceView: UIImageView? { get } - var largeImageController: LargeImageViewController? { get } - var animationImage: UIImage? { get } - var dismissInteractionController: LargeImageInteractionController? { get } - var isInteractivelyAnimatingDismissal: Bool { get set } -} - -extension LargeImageAnimatableViewController { - func sourceViewFrame(in coordinateSpace: UIView) -> CGRect? { - guard let sourceView = animationSourceView else { return nil } - - var sourceFrame = sourceView.convert(sourceView.bounds, to: coordinateSpace) - if let scrollView = coordinateSpace as? UIScrollView { - let scale = scrollView.zoomScale - let width = sourceFrame.width * scale - let height = sourceFrame.height * scale - let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX - let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY - sourceFrame = CGRect(x: x, y: y, width: width, height: height) - } - - return sourceFrame - } -} - -class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning { - - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { - if UIAccessibility.prefersCrossFadeTransitions { - return 0.2 - } else { - return 0.4 - } - } - - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { - guard let fromVC = transitionContext.viewController(forKey: .from), - let toVC = transitionContext.viewController(forKey: .to) as? LargeImageAnimatableViewController else { - return - } - - if UIAccessibility.prefersCrossFadeTransitions { - animateCrossFadeTransition(using: transitionContext) - return - } - - let containerView = transitionContext.containerView - containerView.addSubview(toVC.view) - - let finalVCFrame = transitionContext.finalFrame(for: toVC) - guard let sourceView = toVC.animationSourceView, - let sourceFrame = toVC.sourceViewFrame(in: fromVC.view), - let image = toVC.animationImage else { - toVC.view.frame = finalVCFrame - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return - } - - // use alpha, because isHidden makes stack views re-layout - sourceView.alpha = 0 - toVC.view.alpha = 0 - toVC.largeImageController?.contentView.isHidden = true - toVC.largeImageController?.setControlsVisible(false, animated: false) - - var finalFrameSize = finalVCFrame.inset(by: toVC.view.safeAreaInsets).size - let newWidth = finalFrameSize.width / image.size.width - let newHeight = finalFrameSize.height / image.size.height - if newHeight < newWidth { - finalFrameSize.width = newHeight * image.size.width - } else { - finalFrameSize.height = newWidth * image.size.height - } - let finalFrame = CGRect(origin: CGPoint(x: finalVCFrame.midX - finalFrameSize.width / 2, y: finalVCFrame.midY - finalFrameSize.height / 2), size: finalFrameSize) - - let imageView = GIFImageView(frame: sourceFrame) - imageView.image = image - if let gifController = (sourceView as? GIFImageView)?.gifController { - gifController.attach(to: imageView) - } - imageView.contentMode = .scaleAspectFill - imageView.layer.cornerRadius = sourceView.layer.cornerRadius - imageView.layer.maskedCorners = sourceView.layer.maskedCorners - imageView.layer.masksToBounds = true - - containerView.addSubview(imageView) - - let duration = transitionDuration(using: transitionContext) - let velocity = 1 / CGFloat(duration) - UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.75, initialSpringVelocity: velocity, options: []) { - imageView.frame = finalFrame - imageView.layer.cornerRadius = 0 - toVC.view.alpha = 1 - toVC.largeImageController?.setControlsVisible(true, animated: false) - } completion: { (_) in - // This shouldn't be necessary. I believe it's a workaround for using a XIB - // for the large image VC. Without this, the final frame of the large image VC - // is not set to the propper rect (it uses the frame of the preview device - // in the XIB). When using a storyboard, the final frame is automatically set - // (or UIKit does layout differently when loading the view) and this is not necessary. - toVC.view.frame = finalVCFrame - - toVC.largeImageController?.contentView.isHidden = false - fromVC.view.isHidden = false - imageView.removeFromSuperview() - - sourceView.alpha = 1 - - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - } - } - - func animateCrossFadeTransition(using transitionContext: UIViewControllerContextTransitioning) { - guard let toVC = transitionContext.viewController(forKey: .to) as? LargeImageAnimatableViewController else { - return - } - - transitionContext.containerView.addSubview(toVC.view) - toVC.view.alpha = 0 - - let duration = transitionDuration(using: transitionContext) - UIView.animate(withDuration: duration) { - toVC.view.alpha = 1 - } completion: { (_) in - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - } - } - -} diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageInteractionController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageInteractionController.swift deleted file mode 100644 index 562846a8..00000000 --- a/Tusker/Screens/Large Image/Transitions/LargeImageInteractionController.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// LargeImageInteractionController.swift -// Tusker -// -// Created by Shadowfacts on 9/1/18. -// Copyright © 2018 Shadowfacts. All rights reserved. -// - -import UIKit - -class LargeImageInteractionController: UIPercentDrivenInteractiveTransition { - - var inProgress = false - var direction: CGFloat? - - var shouldCompleteTransition = false - private weak var viewController: LargeImageAnimatableViewController! - - init(viewController: LargeImageAnimatableViewController) { - super.init() - self.viewController = viewController - let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:))) - panRecognizer.allowedScrollTypesMask = .continuous - viewController.view.addGestureRecognizer(panRecognizer) - } - - @objc func handleGesture(_ recognizer: UIPanGestureRecognizer) { - guard let recognizerSuperview = recognizer.view?.superview else { - // Assume the gesture has ended b/c we don't have a view/superview anymore. - inProgress = false - direction = nil - cancel() - return - } - let translation = recognizer.translation(in: recognizerSuperview) - var progress = translation.y / 200 - if let direction = direction { - progress *= direction - } else if abs(progress) > 0.01 { - // if the progress is less than +/- 1%, don't set the direction because the translation may be random jitter in the user's finger - direction = progress > 0 ? 1 : -1 - progress = abs(progress) - } - progress = min(max(progress, 0), 1) - let velocity = abs(recognizer.velocity(in: recognizer.view!.superview!).y) - - switch recognizer.state { - case .began: - inProgress = true - viewController.dismiss(animated: true) - case .changed: - shouldCompleteTransition = progress > 0.5 || velocity > 1000 - viewController.isInteractivelyAnimatingDismissal = progress > 0.1 - update(progress) - case .cancelled: - inProgress = false - cancel() - case .ended: - inProgress = false - direction = nil - if shouldCompleteTransition { - finish() - } else { - cancel() - } - default: - break - } - } - - override func cancel() { - super.cancel() - viewController?.isInteractivelyAnimatingDismissal = false - } - -} diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift deleted file mode 100644 index e7366beb..00000000 --- a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// LargeImageShrinkAnimationController.swift -// Tusker -// -// Created by Shadowfacts on 9/1/18. -// Copyright © 2018 Shadowfacts. All rights reserved. -// - -import UIKit -import TuskerComponents - -class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning { - - let interactionController: LargeImageInteractionController? - - init(interactionController: LargeImageInteractionController?) { - self.interactionController = interactionController - } - - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { - return 0.2 - } - - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { - guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageAnimatableViewController, - let toVC = transitionContext.viewController(forKey: .to) else { - return - } - - if UIAccessibility.prefersCrossFadeTransitions && !transitionContext.isInteractive { - animateCrossFadeTransition(using: transitionContext) - return - } - - guard let sourceView = fromVC.animationSourceView, - let sourceFrame = fromVC.sourceViewFrame(in: toVC.view), - let image = fromVC.animationImage else { - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return - } - - // use alpha, becaus isHidden makes stack views re-layout - sourceView.alpha = 0 - - let containerView = transitionContext.containerView - - let originalVCFrame = fromVC.view.frame - var originalFrameSize = originalVCFrame.inset(by: fromVC.view.safeAreaInsets).size - let newWidth = originalFrameSize.width / image.size.width - let newHeight = originalFrameSize.height / image.size.height - if newHeight < newWidth { - originalFrameSize.width = newHeight * image.size.width - } else { - originalFrameSize.height = newWidth * image.size.height - } - let originalFrame = CGRect(origin: CGPoint(x: originalVCFrame.midX - originalFrameSize.width / 2, y: originalVCFrame.midY - originalFrameSize.height / 2), size: originalFrameSize) - - let imageView = GIFImageView(frame: originalFrame) - imageView.image = image - if let gifController = (sourceView as? GIFImageView)?.gifController { - gifController.attach(to: imageView) - // todo: this might not be necessary, the large image content view should have started it - gifController.startAnimating() - } - imageView.contentMode = .scaleAspectFill - imageView.layer.cornerRadius = 0 - imageView.layer.masksToBounds = true - - let blackView = UIView(frame: originalVCFrame) - blackView.backgroundColor = .black - blackView.alpha = 1 - - // Save the old superview of toVC so we can move it back after the animation. - // It would be better to just not move toVC and instead remove from fromVC from the - // containerView so that toVC is visible behind it, but that doens't work for some reason - // (the entire screen just goes black, not even the contents of the containerView appear). - let oldSuperview = toVC.view.superview - - containerView.addSubview(toVC.view) - containerView.addSubview(blackView) - containerView.addSubview(imageView) - - let duration = transitionDuration(using: transitionContext) - UIView.animate(withDuration: duration, animations: { - imageView.frame = sourceFrame - imageView.layer.cornerRadius = sourceView.layer.cornerRadius - imageView.layer.maskedCorners = sourceView.layer.maskedCorners - blackView.alpha = 0 - }, completion: { _ in - blackView.removeFromSuperview() - imageView.removeFromSuperview() - if transitionContext.transitionWasCancelled { - toVC.view.removeFromSuperview() - } - - sourceView.alpha = 1 - - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - - // move the toVC back to the view that it was in before - oldSuperview?.addSubview(toVC.view) - }) - } - - func animateCrossFadeTransition(using transitionContext: UIViewControllerContextTransitioning) { - guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageAnimatableViewController, - let toVC = transitionContext.viewController(forKey: .to) else { - return - } - - transitionContext.containerView.addSubview(toVC.view) - transitionContext.containerView.addSubview(fromVC.view) - - let duration = transitionDuration(using: transitionContext) - UIView.animate(withDuration: duration) { - fromVC.view.alpha = 0 - } completion: { (_) in - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - } - } - -} diff --git a/Tusker/Screens/Utilities/CustomAlertController.swift b/Tusker/Screens/Utilities/CustomAlertController.swift index d74edd3e..81a67761 100644 --- a/Tusker/Screens/Utilities/CustomAlertController.swift +++ b/Tusker/Screens/Utilities/CustomAlertController.swift @@ -474,7 +474,6 @@ class CustomAlertActionButton: UIControl { } } -#if os(visionOS) extension CustomAlertController: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return CustomAlertPresentationAnimation() @@ -484,17 +483,6 @@ extension CustomAlertController: UIViewControllerTransitioningDelegate { return CustomAlertDismissAnimation() } } -#else -extension CustomAlertController { - override func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - return CustomAlertPresentationAnimation() - } - - override func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - return CustomAlertDismissAnimation() - } -} -#endif class CustomAlertPresentationAnimation: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { diff --git a/Tusker/Screens/Utilities/Previewing.swift b/Tusker/Screens/Utilities/Previewing.swift index 90fbb617..39695bb0 100644 --- a/Tusker/Screens/Utilities/Previewing.swift +++ b/Tusker/Screens/Utilities/Previewing.swift @@ -651,18 +651,6 @@ struct MenuPreviewHelper { } } -extension LargeImageViewController: CustomPreviewPresenting { - func presentFromPreview(presenter: UIViewController) { - presenter.present(self, animated: true) - } -} - -extension GalleryViewController: CustomPreviewPresenting { - func presentFromPreview(presenter: UIViewController) { - presenter.present(self, animated: true) - } -} - extension SFSafariViewController: CustomPreviewPresenting { func presentFromPreview(presenter: UIViewController) { presenter.present(self, animated: true) diff --git a/Tusker/TuskerNavigationDelegate.swift b/Tusker/TuskerNavigationDelegate.swift index e76fb625..8432cbe1 100644 --- a/Tusker/TuskerNavigationDelegate.swift +++ b/Tusker/TuskerNavigationDelegate.swift @@ -20,7 +20,7 @@ protocol TuskerNavigationDelegate: UIViewController, ToastableViewController { extension TuskerNavigationDelegate { func show(_ vc: UIViewController) { - if vc is LargeImageViewController || vc is GalleryViewController || vc is SFSafariViewController { + if vc is SFSafariViewController { present(vc, animated: true) } else { show(vc, sender: self) diff --git a/Tusker/Views/Attachments/AttachmentView.swift b/Tusker/Views/Attachments/AttachmentView.swift index 6eb9343a..44b6f2a5 100644 --- a/Tusker/Views/Attachments/AttachmentView.swift +++ b/Tusker/Views/Attachments/AttachmentView.swift @@ -443,11 +443,15 @@ fileprivate extension AttachmentView { extension AttachmentView: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { return UIContextMenuConfiguration { [unowned self] () -> UIViewController? in - if self.attachment.kind == .image { - return AttachmentPreviewViewController(sourceView: self) - } else if self.attachment.kind == .gifv { - let vc = GifvAttachmentViewController(attachment: self.attachment) - vc.preferredContentSize = self.image?.size ?? .zero + if self.attachment.kind == .image, + let image { + return ImageGalleryContentViewController(url: self.attachment.url, caption: nil, originalData: nil, image: image, gifController: self.gifController) + } else if self.attachment.kind == .gifv, + let gifvView { + return GifvGalleryContentViewController(controller: gifvView.controller, caption: nil) + } else if self.attachment.kind == .video || self.attachment.kind == .audio { + let vc = VideoGalleryContentViewController(url: self.attachment.url, caption: nil) + vc.player.isMuted = true return vc } else { return self.delegate?.attachmentViewGallery(startingAt: self.index) @@ -475,7 +479,7 @@ extension AttachmentView: UIContextMenuInteractionDelegate { itemData = Task { data } } } else if self.attachment.kind == .gifv { - itemSource = GifvActivityItemSource(asset: AVAsset(url: self.attachment.url), attachment: self.attachment) + itemSource = GifvActivityItemSource(asset: AVAsset(url: self.attachment.url), url: self.attachment.url) itemData = Task { try? await URLSession.shared.data(from: self.attachment.url).0 } @@ -509,11 +513,7 @@ extension AttachmentView: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { animator.addCompletion { animator.preferredCommitStyle = .pop - if let gallery = animator.previewViewController as? GalleryViewController { - self.delegate?.attachmentViewPresent(gallery, animated: true) - } else { - self.showGallery() - } + self.showGallery() } } }