Compare commits
No commits in common. "5de0c034f47a93f74c23cad269fa1fff1b263f18" and "6a5753fac8bfd6a60dd3e5fa0b44815df3e2bcdb" have entirely different histories.
5de0c034f4
...
6a5753fac8
|
@ -95,9 +95,6 @@
|
||||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
||||||
D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; };
|
D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; };
|
||||||
D63CC702290EC0B8000E19DE /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = D63CC701290EC0B8000E19DE /* Sentry */; };
|
D63CC702290EC0B8000E19DE /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = D63CC701290EC0B8000E19DE /* Sentry */; };
|
||||||
D63CC70C2910AADB000E19DE /* TuskerSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */; };
|
|
||||||
D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */; };
|
|
||||||
D63CC7122911F57C000E19DE /* StatusBarTappableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */; };
|
|
||||||
D63D8DF42850FE7A008D95E1 /* ViewTags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63D8DF32850FE7A008D95E1 /* ViewTags.swift */; };
|
D63D8DF42850FE7A008D95E1 /* ViewTags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63D8DF32850FE7A008D95E1 /* ViewTags.swift */; };
|
||||||
D63F9C6E241D2D85004C03CF /* CompositionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */; };
|
D63F9C6E241D2D85004C03CF /* CompositionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */; };
|
||||||
D6403CC224A6B72D00E81C55 /* VisualEffectImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */; };
|
D6403CC224A6B72D00E81C55 /* VisualEffectImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */; };
|
||||||
|
@ -125,6 +122,7 @@
|
||||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
||||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
||||||
|
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */; };
|
||||||
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
|
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
|
||||||
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
||||||
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; };
|
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; };
|
||||||
|
@ -449,9 +447,6 @@
|
||||||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
|
||||||
D6370B9B24421FF30092A7FF /* Tusker.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Tusker.xcdatamodel; sourceTree = "<group>"; };
|
D6370B9B24421FF30092A7FF /* Tusker.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Tusker.xcdatamodel; sourceTree = "<group>"; };
|
||||||
D63CC703290EC472000E19DE /* Dist.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Dist.xcconfig; sourceTree = "<group>"; };
|
D63CC703290EC472000E19DE /* Dist.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Dist.xcconfig; sourceTree = "<group>"; };
|
||||||
D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerSceneDelegate.swift; sourceTree = "<group>"; };
|
|
||||||
D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Top.swift"; sourceTree = "<group>"; };
|
|
||||||
D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarTappableViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D63D8DF32850FE7A008D95E1 /* ViewTags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTags.swift; sourceTree = "<group>"; };
|
D63D8DF32850FE7A008D95E1 /* ViewTags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTags.swift; sourceTree = "<group>"; };
|
||||||
D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachment.swift; sourceTree = "<group>"; };
|
D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachment.swift; sourceTree = "<group>"; };
|
||||||
D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectImageButton.swift; sourceTree = "<group>"; };
|
D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectImageButton.swift; sourceTree = "<group>"; };
|
||||||
|
@ -479,6 +474,7 @@
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
||||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
||||||
|
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
||||||
D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = "<group>"; };
|
D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -880,17 +876,6 @@
|
||||||
path = CoreData;
|
path = CoreData;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
D63CC70A2910AAC6000E19DE /* Scenes */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */,
|
|
||||||
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */,
|
|
||||||
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */,
|
|
||||||
D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */,
|
|
||||||
);
|
|
||||||
path = Scenes;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D641C780213DD7C4004B4513 /* Screens */ = {
|
D641C780213DD7C4004B4513 /* Screens */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -925,6 +910,7 @@
|
||||||
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */,
|
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */,
|
||||||
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */,
|
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */,
|
||||||
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
||||||
|
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */,
|
||||||
D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */,
|
D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */,
|
||||||
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */,
|
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */,
|
||||||
);
|
);
|
||||||
|
@ -1154,7 +1140,6 @@
|
||||||
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
||||||
D62E9984279CA23900C26176 /* URLSession+Development.swift */,
|
D62E9984279CA23900C26176 /* URLSession+Development.swift */,
|
||||||
D6ADB6ED28EA74E8009924AB /* UIView+Configure.swift */,
|
D6ADB6ED28EA74E8009924AB /* UIView+Configure.swift */,
|
||||||
D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1332,7 +1317,6 @@
|
||||||
D6E0DC8D216EDF1E00369478 /* Previewing.swift */,
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */,
|
||||||
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
||||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
||||||
D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */,
|
|
||||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
||||||
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
||||||
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
||||||
|
@ -1388,11 +1372,14 @@
|
||||||
D6D4DDDB212518A200E1C4BB /* Info.plist */,
|
D6D4DDDB212518A200E1C4BB /* Info.plist */,
|
||||||
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
||||||
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */,
|
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */,
|
||||||
|
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */,
|
||||||
|
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */,
|
||||||
D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */,
|
D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */,
|
||||||
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
||||||
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||||
D61DC84528F498F200B82C6E /* Logging.swift */,
|
D61DC84528F498F200B82C6E /* Logging.swift */,
|
||||||
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */,
|
||||||
D6B81F432560390300F6E31D /* MenuController.swift */,
|
D6B81F432560390300F6E31D /* MenuController.swift */,
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
|
@ -1410,7 +1397,6 @@
|
||||||
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */,
|
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */,
|
||||||
D61959D2241E846D00A37B8E /* Models */,
|
D61959D2241E846D00A37B8E /* Models */,
|
||||||
D663626021360A9600C9CBA2 /* Preferences */,
|
D663626021360A9600C9CBA2 /* Preferences */,
|
||||||
D63CC70A2910AAC6000E19DE /* Scenes */,
|
|
||||||
D641C780213DD7C4004B4513 /* Screens */,
|
D641C780213DD7C4004B4513 /* Screens */,
|
||||||
D62D241E217AA46B005076CC /* Shortcuts */,
|
D62D241E217AA46B005076CC /* Shortcuts */,
|
||||||
D67B506B250B28FF00FAECFB /* Vendor */,
|
D67B506B250B28FF00FAECFB /* Vendor */,
|
||||||
|
@ -1796,7 +1782,6 @@
|
||||||
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
||||||
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
||||||
D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */,
|
D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */,
|
||||||
D63CC7122911F57C000E19DE /* StatusBarTappableViewController.swift in Sources */,
|
|
||||||
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */,
|
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */,
|
||||||
D6D3FDE224F46A8D00FF50A5 /* ComposeUIState.swift in Sources */,
|
D6D3FDE224F46A8D00FF50A5 /* ComposeUIState.swift in Sources */,
|
||||||
D6B22A0F2560D52D004D82EF /* TabbedPageViewController.swift in Sources */,
|
D6B22A0F2560D52D004D82EF /* TabbedPageViewController.swift in Sources */,
|
||||||
|
@ -1827,7 +1812,6 @@
|
||||||
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
||||||
D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */,
|
D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */,
|
||||||
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */,
|
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */,
|
||||||
D63CC70C2910AADB000E19DE /* TuskerSceneDelegate.swift in Sources */,
|
|
||||||
D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */,
|
D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */,
|
||||||
D6B9366D2828445000237D0E /* SavedInstance.swift in Sources */,
|
D6B9366D2828445000237D0E /* SavedInstance.swift in Sources */,
|
||||||
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */,
|
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */,
|
||||||
|
@ -1891,7 +1875,6 @@
|
||||||
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */,
|
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */,
|
||||||
D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */,
|
D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */,
|
||||||
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
||||||
D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */,
|
|
||||||
D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */,
|
D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */,
|
||||||
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */,
|
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */,
|
||||||
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */,
|
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */,
|
||||||
|
@ -1994,6 +1977,7 @@
|
||||||
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */,
|
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */,
|
||||||
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */,
|
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */,
|
||||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */,
|
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */,
|
||||||
|
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */,
|
||||||
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */,
|
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */,
|
||||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */,
|
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */,
|
||||||
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */,
|
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */,
|
||||||
|
|
|
@ -18,8 +18,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
|
||||||
configureSentry()
|
configureSentry()
|
||||||
swizzleStatusBar()
|
|
||||||
|
|
||||||
AppShortcutItem.createItems(for: application)
|
AppShortcutItem.createItems(for: application)
|
||||||
|
|
||||||
|
@ -129,28 +129,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
}
|
}
|
||||||
UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil)
|
UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func swizzleStatusBar() {
|
|
||||||
let selector = Selector(("handleTapAction:"))
|
|
||||||
var originalIMP: IMP?
|
|
||||||
let imp = imp_implementationWithBlock({ (self: UIStatusBarManager, sender: AnyObject) in
|
|
||||||
let original = unsafeBitCast(originalIMP!, to: (@convention(c) (UIStatusBarManager, Selector, AnyObject) -> Void).self)
|
|
||||||
guard let windowScene = self.perform(Selector(("windowScene"))).takeUnretainedValue() as? UIWindowScene,
|
|
||||||
let xPosition = sender.value(forKey: "xPosition") as? CGFloat,
|
|
||||||
let delegate = windowScene.delegate as? TuskerSceneDelegate else {
|
|
||||||
original(self, selector, sender)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch delegate.handleStatusBarTapped(xPosition: xPosition) {
|
|
||||||
case .stop:
|
|
||||||
return
|
|
||||||
case .continue:
|
|
||||||
original(self, selector, sender)
|
|
||||||
}
|
|
||||||
} as @convention(block) (UIStatusBarManager, AnyObject) -> Void)
|
|
||||||
originalIMP = class_replaceMethod(UIStatusBarManager.self, selector, imp, "v@:@")
|
|
||||||
if originalIMP == nil {
|
|
||||||
Logging.general.error("Unable to swizzle status bar manager")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
switch timeline {
|
switch timeline {
|
||||||
// todo: list/hashtag controllers need whole objects which must be fetched asynchronously
|
// todo: list/hashtag controllers need whole objects which must be fetched asynchronously
|
||||||
default:
|
default:
|
||||||
return TimelineViewController(for: timeline, mastodonController: mastodonController)
|
return TimelineTableViewController(for: timeline, mastodonController: mastodonController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
//
|
|
||||||
// UIScrollView+Top.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 11/1/22.
|
|
||||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
private var prevScrollOffsetBeforeScrollToTopKey: Void = ()
|
|
||||||
|
|
||||||
extension UIScrollView {
|
|
||||||
private var prevScrollOffsetBeforeScrollToTop: CGFloat? {
|
|
||||||
get {
|
|
||||||
if let v = (objc_getAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey) as? NSNumber)?.doubleValue {
|
|
||||||
return CGFloat(v)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if let newValue {
|
|
||||||
objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_COPY_NONATOMIC)
|
|
||||||
} else {
|
|
||||||
objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, nil, .OBJC_ASSOCIATION_COPY_NONATOMIC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func scrollToTop() {
|
|
||||||
let top = -adjustedContentInset.top
|
|
||||||
// +5 to add a little bit of wiggle room
|
|
||||||
let isScrolledToTop = contentOffset.y < top + 5
|
|
||||||
if isScrolledToTop {
|
|
||||||
if let prevScrollOffsetBeforeScrollToTop {
|
|
||||||
self.prevScrollOffsetBeforeScrollToTop = nil
|
|
||||||
setContentOffset(CGPoint(x: 0, y: prevScrollOffsetBeforeScrollToTop), animated: true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
prevScrollOffsetBeforeScrollToTop = contentOffset.y
|
|
||||||
setContentOffset(CGPoint(x: 0, y: top), animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import Pachyderm
|
||||||
import MessageUI
|
import MessageUI
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate {
|
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// TuskerSceneDelegate.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 10/31/22.
|
|
||||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol TuskerSceneDelegate: UISceneDelegate {
|
|
||||||
var rootViewController: TuskerRootViewController? { get }
|
|
||||||
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult
|
|
||||||
}
|
|
||||||
|
|
||||||
enum StatusBarTapActionResult {
|
|
||||||
case `continue`
|
|
||||||
case stop
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TuskerSceneDelegate {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
if let rootViewController {
|
|
||||||
let converted = rootViewController.view.convert(CGPoint(x: xPosition, y: 0), from: nil)
|
|
||||||
return rootViewController.handleStatusBarTapped(xPosition: converted.x)
|
|
||||||
}
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class ListTimelineViewController: TimelineViewController {
|
class ListTimelineViewController: TimelineTableViewController {
|
||||||
|
|
||||||
let list: List
|
let list: List
|
||||||
|
|
||||||
|
@ -57,11 +57,8 @@ class ListTimelineViewController: TimelineViewController {
|
||||||
@objc func editListDoneButtonPressed() {
|
@objc func editListDoneButtonPressed() {
|
||||||
dismiss(animated: true)
|
dismiss(animated: true)
|
||||||
|
|
||||||
// TODO: only reload if there were changes
|
// todo: show loading indicator
|
||||||
Task {
|
reloadInitial()
|
||||||
applyInitialSnapshot()
|
|
||||||
await controller.loadInitial()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,12 +111,6 @@ extension AccountSwitchingContainerViewController: TuskerRootViewController {
|
||||||
loadViewIfNeeded()
|
loadViewIfNeeded()
|
||||||
root.presentPreferences(completion: completion)
|
root.presentPreferences(completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
loadViewIfNeeded()
|
|
||||||
// TODO: check if fast account switcher is being presented?
|
|
||||||
return root.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountSwitchingContainerViewController: BackgroundableViewController {
|
extension AccountSwitchingContainerViewController: BackgroundableViewController {
|
||||||
|
|
|
@ -456,22 +456,6 @@ extension MainSplitViewController: TuskerRootViewController {
|
||||||
func presentPreferences(completion: (() -> Void)?) {
|
func presentPreferences(completion: (() -> Void)?) {
|
||||||
present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion)
|
present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
guard presentedViewController == nil else {
|
|
||||||
return .stop
|
|
||||||
}
|
|
||||||
if traitCollection.horizontalSizeClass == .compact {
|
|
||||||
return tabBarViewController.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
} else {
|
|
||||||
let pointInSecondary = secondaryNavController.view.convert(CGPoint(x: xPosition, y: 0), from: view)
|
|
||||||
if secondaryNavController.view.bounds.contains(pointInSecondary) {
|
|
||||||
return secondaryNavController.handleStatusBarTapped(xPosition: pointInSecondary.x)
|
|
||||||
} else {
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainSplitViewController: BackgroundableViewController {
|
extension MainSplitViewController: BackgroundableViewController {
|
||||||
|
|
|
@ -285,16 +285,6 @@ extension MainTabBarViewController: TuskerRootViewController {
|
||||||
func presentPreferences(completion: (() -> Void)?) {
|
func presentPreferences(completion: (() -> Void)?) {
|
||||||
present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion)
|
present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
guard presentedViewController == nil else {
|
|
||||||
return .stop
|
|
||||||
}
|
|
||||||
guard let vc = viewController(for: selectedTab) as? StatusBarTappableViewController else {
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
return vc.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainTabBarViewController: BackgroundableViewController {
|
extension MainTabBarViewController: BackgroundableViewController {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol TuskerRootViewController: UIViewController, StatusBarTappableViewController {
|
protocol TuskerRootViewController: UIViewController {
|
||||||
func presentCompose()
|
func presentCompose()
|
||||||
func select(tab: MainTabBarViewController.Tab)
|
func select(tab: MainTabBarViewController.Tab)
|
||||||
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController?
|
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController?
|
||||||
|
|
|
@ -470,16 +470,3 @@ extension ProfileStatusesViewController: StatusCollectionViewCellDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileStatusesViewController: TabBarScrollableViewController {
|
|
||||||
func tabBarScrollToTop() {
|
|
||||||
collectionView.scrollToTop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProfileStatusesViewController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
collectionView.scrollToTop()
|
|
||||||
return .stop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -307,15 +307,3 @@ extension ProfileViewController: TabbedPageViewController {
|
||||||
selectPage(at: currentIndex - 1, animated: true)
|
selectPage(at: currentIndex - 1, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileViewController: TabBarScrollableViewController {
|
|
||||||
func tabBarScrollToTop() {
|
|
||||||
currentViewController.tabBarScrollToTop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProfileViewController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
return currentViewController.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class HashtagTimelineViewController: TimelineViewController {
|
class HashtagTimelineViewController: TimelineTableViewController {
|
||||||
|
|
||||||
let hashtag: Hashtag
|
let hashtag: Hashtag
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
|
||||||
|
|
||||||
protocol InstanceTimelineViewControllerDelegate: AnyObject {
|
protocol InstanceTimelineViewControllerDelegate: AnyObject {
|
||||||
func didSaveInstance(url: URL)
|
func didSaveInstance(url: URL)
|
||||||
func didUnsaveInstance(url: URL)
|
func didUnsaveInstance(url: URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
class InstanceTimelineViewController: TimelineViewController {
|
class InstanceTimelineViewController: TimelineTableViewController {
|
||||||
|
|
||||||
weak var delegate: InstanceTimelineViewControllerDelegate?
|
weak var delegate: InstanceTimelineViewControllerDelegate?
|
||||||
|
|
||||||
|
@ -69,15 +68,19 @@ class InstanceTimelineViewController: TimelineViewController {
|
||||||
toggleSaveButton.title = toggleSaveButtonTitle
|
toggleSaveButton.title = toggleSaveButtonTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
override func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) {
|
// MARK: - Table view data source
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let cell = super.tableView(tableView, cellForRowAt: indexPath) as! TimelineStatusTableViewCell
|
||||||
cell.delegate = browsingEnabled ? self : nil
|
cell.delegate = browsingEnabled ? self : nil
|
||||||
cell.overrideMastodonController = mastodonController
|
return cell
|
||||||
cell.updateUI(statusID: id, state: state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard browsingEnabled else { return }
|
guard browsingEnabled else { return }
|
||||||
super.collectionView(collectionView, didSelectItemAt: indexPath)
|
super.tableView(tableView, didSelectRowAt: indexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Interaction
|
// MARK: - Interaction
|
||||||
|
|
|
@ -0,0 +1,334 @@
|
||||||
|
//
|
||||||
|
// TimelineTableViewController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/15/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
typealias TimelineEntry = (id: String, state: StatusState)
|
||||||
|
|
||||||
|
class TimelineTableViewController: DiffableTimelineLikeTableViewController<TimelineTableViewController.Section, TimelineTableViewController.Item> {
|
||||||
|
|
||||||
|
let timeline: Timeline
|
||||||
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
|
private var newer: RequestRange?
|
||||||
|
private var older: RequestRange?
|
||||||
|
|
||||||
|
private var didConfirmLoadMore = false
|
||||||
|
private var isShowingTimelineDescription = false
|
||||||
|
|
||||||
|
init(for timeline: Timeline, mastodonController: MastodonController) {
|
||||||
|
self.timeline = timeline
|
||||||
|
self.mastodonController = mastodonController
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
dragEnabled = true
|
||||||
|
|
||||||
|
title = timeline.title
|
||||||
|
tabBarItem.image = timeline.tabBarImage
|
||||||
|
|
||||||
|
if let id = mastodonController.accountInfo?.id {
|
||||||
|
userActivity = UserActivityManager.showTimelineActivity(timeline: timeline, accountID: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell")
|
||||||
|
tableView.register(UINib(nibName: "ConfirmLoadMoreTableViewCell", bundle: .main), forCellReuseIdentifier: "confirmLoadMoreCell")
|
||||||
|
tableView.register(UINib(nibName: "PublicTimelineDescriptionTableViewCell", bundle: .main), forCellReuseIdentifier: "publicTimelineDescriptionCell")
|
||||||
|
|
||||||
|
if case let .public(local: local) = timeline,
|
||||||
|
(local && !Preferences.shared.hasShownLocalTimelineDescription) || (!local && !Preferences.shared.hasShownFederatedTimelineDescription) {
|
||||||
|
isShowingTimelineDescription = true
|
||||||
|
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
snapshot.appendSections([.header])
|
||||||
|
snapshot.appendItems([.publicTimelineDescription(local: local)], toSection: .header)
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if case let .public(local: local) = timeline {
|
||||||
|
if local {
|
||||||
|
Preferences.shared.hasShownLocalTimelineDescription = true
|
||||||
|
} else {
|
||||||
|
Preferences.shared.hasShownFederatedTimelineDescription = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
|
if isShowingTimelineDescription {
|
||||||
|
isShowingTimelineDescription = false
|
||||||
|
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
snapshot.deleteSections([.header])
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - DiffableTimelineLikeTableViewController
|
||||||
|
|
||||||
|
override class func refreshCommandTitle() -> String {
|
||||||
|
return NSLocalizedString("Refresh Statuses", comment: "refresh status command discoverability title")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func timelineContentSections() -> [Section] {
|
||||||
|
return [.statuses]
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||||
|
switch item {
|
||||||
|
case .loadingIndicator:
|
||||||
|
return self.loadingIndicatorCell(indexPath: indexPath)
|
||||||
|
|
||||||
|
case let .status(id: id, state: state):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
||||||
|
|
||||||
|
cell.delegate = self
|
||||||
|
cell.updateUI(statusID: id, state: state)
|
||||||
|
return cell
|
||||||
|
|
||||||
|
case .confirmLoadMore:
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "confirmLoadMoreCell", for: indexPath) as! ConfirmLoadMoreTableViewCell
|
||||||
|
cell.confirmLoadMore = {
|
||||||
|
self.didConfirmLoadMore = true
|
||||||
|
self.loadOlder()
|
||||||
|
self.didConfirmLoadMore = false
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
|
||||||
|
case .publicTimelineDescription(local: let local):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "publicTimelineDescriptionCell", for: indexPath) as! PublicTimelineDescriptionTableViewCell
|
||||||
|
cell.mastodonController = mastodonController
|
||||||
|
cell.local = local
|
||||||
|
cell.didDismiss = { [unowned self] in
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
snapshot.deleteSections([.header])
|
||||||
|
self.dataSource.apply(snapshot)
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadInitialItems(completion: @escaping (LoadResult) -> Void) {
|
||||||
|
guard let mastodonController = mastodonController else {
|
||||||
|
completion(.failure(.noClient))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = Client.getStatuses(timeline: timeline)
|
||||||
|
|
||||||
|
mastodonController.run(request) { response in
|
||||||
|
switch response {
|
||||||
|
case let .failure(error):
|
||||||
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, _):
|
||||||
|
if !statuses.isEmpty {
|
||||||
|
self.newer = .after(id: statuses.first!.id, count: nil)
|
||||||
|
self.older = .before(id: statuses.last!.id, count: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
if snapshot.sectionIdentifiers.contains(.loadingIndicator) {
|
||||||
|
snapshot.deleteSections([.loadingIndicator])
|
||||||
|
}
|
||||||
|
snapshot.deleteSections([.statuses, .footer])
|
||||||
|
snapshot.appendSections([.statuses, .footer])
|
||||||
|
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadOlderItems(currentSnapshot: @escaping () -> Snapshot, completion: @escaping (LoadResult) -> Void) {
|
||||||
|
guard let older = older else {
|
||||||
|
completion(.failure(.noOlder))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if Preferences.shared.disableInfiniteScrolling && !didConfirmLoadMore {
|
||||||
|
var snapshot = currentSnapshot()
|
||||||
|
guard !snapshot.itemIdentifiers(inSection: .footer).contains(.confirmLoadMore) else {
|
||||||
|
// todo: need something more accurate than "success"/"failure"
|
||||||
|
completion(.success(snapshot))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
snapshot.appendItems([.confirmLoadMore], toSection: .footer)
|
||||||
|
completion(.success(snapshot))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = Client.getStatuses(timeline: timeline, range: older)
|
||||||
|
|
||||||
|
mastodonController.run(request) { response in
|
||||||
|
switch response {
|
||||||
|
case let .failure(error):
|
||||||
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, _):
|
||||||
|
if !statuses.isEmpty {
|
||||||
|
self.older = .before(id: statuses.last!.id, count: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
|
var snapshot = currentSnapshot()
|
||||||
|
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||||
|
snapshot.deleteItems([.confirmLoadMore])
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadNewerItems(currentSnapshot: @escaping () -> Snapshot, completion: @escaping (LoadResult) -> Void) {
|
||||||
|
guard let newer = newer else {
|
||||||
|
completion(.failure(.noNewer))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = Client.getStatuses(timeline: timeline, range: newer)
|
||||||
|
mastodonController.run(request) { response in
|
||||||
|
switch response {
|
||||||
|
case let .failure(error):
|
||||||
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, _):
|
||||||
|
guard !statuses.isEmpty else {
|
||||||
|
completion(.failure(.allCaughtUp))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.newer = .after(id: statuses.first!.id, count: nil)
|
||||||
|
|
||||||
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
|
var snapshot = currentSnapshot()
|
||||||
|
let newIdentifiers = statuses.map { Item.status(id: $0.id, state: .unknown) }
|
||||||
|
if let first = snapshot.itemIdentifiers(inSection: .statuses).first {
|
||||||
|
snapshot.insertItems(newIdentifiers, beforeItem: first)
|
||||||
|
} else {
|
||||||
|
snapshot.appendItems(newIdentifiers, toSection: .statuses)
|
||||||
|
}
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
super.scrollViewWillBeginDragging(scrollView)
|
||||||
|
|
||||||
|
if isShowingTimelineDescription {
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
snapshot.deleteSections([.header])
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController {
|
||||||
|
enum Section: DiffableTimelineLikeSection {
|
||||||
|
case loadingIndicator
|
||||||
|
case header
|
||||||
|
case statuses
|
||||||
|
case footer
|
||||||
|
}
|
||||||
|
enum Item: DiffableTimelineLikeItem {
|
||||||
|
case loadingIndicator
|
||||||
|
case status(id: String, state: StatusState)
|
||||||
|
case confirmLoadMore
|
||||||
|
case publicTimelineDescription(local: Bool)
|
||||||
|
|
||||||
|
static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case let (.status(id: a, state: _), .status(id: b, state: _)):
|
||||||
|
return a == b
|
||||||
|
case (.confirmLoadMore, .confirmLoadMore):
|
||||||
|
return true
|
||||||
|
case let (.publicTimelineDescription(local: a), .publicTimelineDescription(local: b)):
|
||||||
|
return a == b
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
switch self {
|
||||||
|
case .loadingIndicator:
|
||||||
|
hasher.combine(0)
|
||||||
|
case let .status(id: id, state: _):
|
||||||
|
hasher.combine(1)
|
||||||
|
hasher.combine(id)
|
||||||
|
case .confirmLoadMore:
|
||||||
|
hasher.combine(2)
|
||||||
|
case let .publicTimelineDescription(local: local):
|
||||||
|
hasher.combine(3)
|
||||||
|
hasher.combine(local)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController: TuskerNavigationDelegate {
|
||||||
|
var apiController: MastodonController! { mastodonController }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||||
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
} else {
|
||||||
|
cellHeightChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController: MenuActionProvider {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||||
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
|
let ids: [String] = indexPaths.compactMap {
|
||||||
|
if case let .status(id: id, state: _) = dataSource.itemIdentifier(for: $0) {
|
||||||
|
return id
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefetchStatuses(with: ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
|
let ids: [String] = indexPaths.compactMap {
|
||||||
|
if case let .status(id: id, state: _) = dataSource.itemIdentifier(for: $0) {
|
||||||
|
return id
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelPrefetchingStatuses(with: ids)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
// TODO: gonna need a thing to replicate all of EnhancedTableViewController
|
||||||
|
|
||||||
class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, RefreshableViewController {
|
class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, RefreshableViewController {
|
||||||
let timeline: Timeline
|
let timeline: Timeline
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
@ -34,7 +36,6 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
|
|
||||||
self.controller = TimelineLikeController(delegate: self)
|
self.controller = TimelineLikeController(delegate: self)
|
||||||
|
|
||||||
self.navigationItem.title = timeline.title
|
|
||||||
addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh Timeline"))
|
addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh Timeline"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,15 +86,10 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
// separate method because InstanceTimelineViewController needs to be able to customize it
|
|
||||||
func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) {
|
|
||||||
cell.delegate = self
|
|
||||||
cell.updateUI(statusID: id, state: state)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in
|
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in
|
||||||
self.configureStatusCell(cell, id: item.0, state: item.1)
|
cell.delegate = self
|
||||||
|
cell.updateUI(statusID: item.0, state: item.1)
|
||||||
}
|
}
|
||||||
let timelineDescriptionCell = UICollectionView.CellRegistration<PublicTimelineDescriptionCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
|
let timelineDescriptionCell = UICollectionView.CellRegistration<PublicTimelineDescriptionCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
|
||||||
guard case .public(let local) = timeline else {
|
guard case .public(let local) = timeline else {
|
||||||
|
@ -120,16 +116,15 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-private, because ListTimelineViewController needs to be able to reload it from scratch
|
private func applyInitialSnapshot() {
|
||||||
func applyInitialSnapshot() {
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
|
||||||
if case .public(let local) = timeline,
|
if case .public(let local) = timeline,
|
||||||
(local && !Preferences.shared.hasShownLocalTimelineDescription) ||
|
(local && !Preferences.shared.hasShownLocalTimelineDescription) ||
|
||||||
(!local && !Preferences.shared.hasShownFederatedTimelineDescription) {
|
(!local && !Preferences.shared.hasShownFederatedTimelineDescription) {
|
||||||
|
var snapshot = dataSource.snapshot()
|
||||||
snapshot.appendSections([.header])
|
snapshot.appendSections([.header])
|
||||||
snapshot.appendItems([.publicTimelineDescription], toSection: .header)
|
snapshot.appendItems([.publicTimelineDescription], toSection: .header)
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
dataSource.apply(snapshot, animatingDifferences: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -418,16 +413,3 @@ extension TimelineViewController: StatusCollectionViewCellDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineViewController: TabBarScrollableViewController {
|
|
||||||
func tabBarScrollToTop() {
|
|
||||||
collectionView.scrollToTop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TimelineViewController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
collectionView.scrollToTop()
|
|
||||||
return .stop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -248,12 +248,3 @@ extension EnhancedNavigationViewController: BackgroundableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EnhancedNavigationViewController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
if let topVC = topViewController as? StatusBarTappableViewController {
|
|
||||||
return topVC.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
}
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,6 +11,11 @@ import SafariServices
|
||||||
|
|
||||||
class EnhancedTableViewController: UITableViewController {
|
class EnhancedTableViewController: UITableViewController {
|
||||||
|
|
||||||
|
private var prevScrollToTopOffset: CGPoint? = nil
|
||||||
|
private(set) var isCurrentlyScrollingToTop = false
|
||||||
|
private var prevScrollViewContentOffset: CGPoint?
|
||||||
|
private(set) var scrollViewDirection: CGFloat = 0
|
||||||
|
|
||||||
var dragEnabled = false
|
var dragEnabled = false
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
@ -21,6 +26,38 @@ class EnhancedTableViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Scroll View Delegate
|
||||||
|
|
||||||
|
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
|
||||||
|
if let offset = prevScrollToTopOffset {
|
||||||
|
tableView.setContentOffset(offset, animated: true)
|
||||||
|
prevScrollToTopOffset = nil
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
prevScrollToTopOffset = tableView.contentOffset
|
||||||
|
isCurrentlyScrollingToTop = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
|
||||||
|
isCurrentlyScrollingToTop = false
|
||||||
|
// add one so it's not technically scrolled all the way to the top,
|
||||||
|
// otherwise there's no way of detecting a status bar press to scroll back down
|
||||||
|
tableView.contentOffset.y -= 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
prevScrollToTopOffset = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if let prev = prevScrollViewContentOffset {
|
||||||
|
scrollViewDirection = scrollView.contentOffset.y - prev.y
|
||||||
|
}
|
||||||
|
prevScrollViewContentOffset = scrollView.contentOffset
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Table View Delegate
|
// MARK: Table View Delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
@ -80,13 +117,10 @@ extension EnhancedTableViewController: UITableViewDragDelegate {
|
||||||
|
|
||||||
extension EnhancedTableViewController: TabBarScrollableViewController {
|
extension EnhancedTableViewController: TabBarScrollableViewController {
|
||||||
func tabBarScrollToTop() {
|
func tabBarScrollToTop() {
|
||||||
tableView.scrollToTop()
|
if scrollViewShouldScrollToTop(tableView) {
|
||||||
}
|
let topOffset = CGPoint(x: 0, y: -tableView.adjustedContentInset.top)
|
||||||
}
|
tableView.setContentOffset(topOffset, animated: true)
|
||||||
|
scrollViewDidScrollToTop(tableView)
|
||||||
extension EnhancedTableViewController: StatusBarTappableViewController {
|
}
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
tableView.scrollToTop()
|
|
||||||
return .stop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,12 +105,3 @@ extension SegmentedPageViewController: BackgroundableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SegmentedPageViewController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
if let current = pageControllers[currentIndex] as? StatusBarTappableViewController {
|
|
||||||
return current.handleStatusBarTapped(xPosition: xPosition)
|
|
||||||
}
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -135,6 +135,12 @@ class SplitNavigationController: UIViewController {
|
||||||
updateSecondaryNavVisibility()
|
updateSecondaryNavVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override func viewWillLayoutSubviews() {
|
override func viewWillLayoutSubviews() {
|
||||||
super.viewWillLayoutSubviews()
|
super.viewWillLayoutSubviews()
|
||||||
|
|
||||||
|
@ -239,24 +245,7 @@ class SplitNavigationController: UIViewController {
|
||||||
self.updateSecondaryNavVisibility()
|
self.updateSecondaryNavVisibility()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension SplitNavigationController: StatusBarTappableViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
|
|
||||||
let vcs = viewControllers
|
|
||||||
if !canShowSecondaryNav || vcs.count < 2 {
|
|
||||||
return (vcs.first! as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: xPosition) ?? .continue
|
|
||||||
} else {
|
|
||||||
let positionInRoot = rootNav.view.convert(CGPoint(x: xPosition, y: 0), from: view)
|
|
||||||
let positionInSecondary = secondaryNav.view.convert(CGPoint(x: xPosition, y: 0), from: view)
|
|
||||||
if rootNav.view.bounds.contains(positionInRoot) {
|
|
||||||
return (rootNav.topViewController as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: positionInRoot.x) ?? .continue
|
|
||||||
} else if secondaryNav.view.bounds.contains(positionInSecondary) {
|
|
||||||
return (secondaryNav.topViewController as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: positionInRoot.x) ?? .continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return .continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SplitRootNavigationController: UINavigationController {
|
private class SplitRootNavigationController: UINavigationController {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
//
|
|
||||||
// StatusBarTappableViewController.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 11/1/22.
|
|
||||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol StatusBarTappableViewController: UIViewController {
|
|
||||||
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult
|
|
||||||
}
|
|
|
@ -205,7 +205,7 @@ class UserActivityManager {
|
||||||
rootController.segmentedControl.selectedSegmentIndex = index
|
rootController.segmentedControl.selectedSegmentIndex = index
|
||||||
rootController.selectPage(at: index, animated: false)
|
rootController.selectPage(at: index, animated: false)
|
||||||
default:
|
default:
|
||||||
let timeline = TimelineViewController(for: timeline, mastodonController: mastodonController)
|
let timeline = TimelineTableViewController(for: timeline, mastodonController: mastodonController)
|
||||||
navigationController.pushViewController(timeline, animated: false)
|
navigationController.pushViewController(timeline, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ actor TimelineLikeController<Item> {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadInitial() async {
|
func loadInitial() async {
|
||||||
guard state == .notLoadedInitial || state == .idle else {
|
guard state == .notLoadedInitial else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let token = LoadAttemptToken()
|
let token = LoadAttemptToken()
|
||||||
|
@ -175,14 +175,14 @@ actor TimelineLikeController<Item> {
|
||||||
switch self {
|
switch self {
|
||||||
case .notLoadedInitial:
|
case .notLoadedInitial:
|
||||||
switch to {
|
switch to {
|
||||||
case .loadingInitial(_, _):
|
case .loadingInitial(_, hasAddedLoadingIndicator: _):
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .idle:
|
case .idle:
|
||||||
switch to {
|
switch to {
|
||||||
case .loadingInitial(_, _), .loadingNewer(_), .loadingOlder(_, _):
|
case .loadingNewer(_), .loadingOlder(_, _):
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -30,7 +30,6 @@ protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate
|
||||||
var moreButton: UIButton { get }
|
var moreButton: UIButton { get }
|
||||||
|
|
||||||
var delegate: StatusCollectionViewCellDelegate? { get }
|
var delegate: StatusCollectionViewCellDelegate? { get }
|
||||||
var mastodonController: MastodonController! { get }
|
|
||||||
|
|
||||||
var showStatusAutomatically: Bool { get }
|
var showStatusAutomatically: Bool { get }
|
||||||
var showReplyIndicator: Bool { get }
|
var showReplyIndicator: Bool { get }
|
||||||
|
@ -76,6 +75,8 @@ extension StatusCollectionViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doUpdateUI(status: StatusMO) {
|
func doUpdateUI(status: StatusMO) {
|
||||||
|
precondition(delegate != nil, "StatusCollectionViewCell must have delegate")
|
||||||
|
|
||||||
statusID = status.id
|
statusID = status.id
|
||||||
accountID = status.account.id
|
accountID = status.account.id
|
||||||
|
|
||||||
|
|
|
@ -227,8 +227,6 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
||||||
private var mainContainerBottomToActionsConstraint: NSLayoutConstraint!
|
private var mainContainerBottomToActionsConstraint: NSLayoutConstraint!
|
||||||
private var mainContainerBottomToSelfConstraint: NSLayoutConstraint!
|
private var mainContainerBottomToSelfConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
weak var overrideMastodonController: MastodonController?
|
|
||||||
var mastodonController: MastodonController! { overrideMastodonController ?? delegate?.apiController }
|
|
||||||
weak var delegate: StatusCollectionViewCellDelegate?
|
weak var delegate: StatusCollectionViewCellDelegate?
|
||||||
var showStatusAutomatically: Bool {
|
var showStatusAutomatically: Bool {
|
||||||
// TODO: needed once conversation controller refactored
|
// TODO: needed once conversation controller refactored
|
||||||
|
|
Loading…
Reference in New Issue