forked from zelo72/mastodon-ios
Merge pull request #430 from mastodon/feature-discovery-keyboard
Add keyboard commands for discovery scene
This commit is contained in:
commit
556bc54c84
|
@ -286,8 +286,6 @@
|
||||||
DB4F097F26A03DA600D62E92 /* SearchHistoryFetchedResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4F097E26A03DA600D62E92 /* SearchHistoryFetchedResultController.swift */; };
|
DB4F097F26A03DA600D62E92 /* SearchHistoryFetchedResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4F097E26A03DA600D62E92 /* SearchHistoryFetchedResultController.swift */; };
|
||||||
DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC29269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift */; };
|
DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC29269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift */; };
|
||||||
DB4FFC2C269EC39600D62E92 /* SearchTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC2A269EC39600D62E92 /* SearchTransitionController.swift */; };
|
DB4FFC2C269EC39600D62E92 /* SearchTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC2A269EC39600D62E92 /* SearchTransitionController.swift */; };
|
||||||
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D170262832380062B7A1 /* BlurHashDecode.swift */; };
|
|
||||||
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D171262832380062B7A1 /* BlurHashEncode.swift */; };
|
|
||||||
DB552D4F26BBD10C00E481F6 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DB552D4E26BBD10C00E481F6 /* OrderedCollections */; };
|
DB552D4F26BBD10C00E481F6 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DB552D4E26BBD10C00E481F6 /* OrderedCollections */; };
|
||||||
DB564BD3269F3B35001E39A7 /* StatusFilterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */; };
|
DB564BD3269F3B35001E39A7 /* StatusFilterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */; };
|
||||||
DB59F10E25EF724F001F1DAB /* APIService+Poll.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */; };
|
DB59F10E25EF724F001F1DAB /* APIService+Poll.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */; };
|
||||||
|
@ -415,7 +413,6 @@
|
||||||
DB852D1F26FB037800FC9D81 /* SidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB852D1E26FB037800FC9D81 /* SidebarViewModel.swift */; };
|
DB852D1F26FB037800FC9D81 /* SidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB852D1E26FB037800FC9D81 /* SidebarViewModel.swift */; };
|
||||||
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; };
|
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */; };
|
||||||
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */; };
|
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */; };
|
||||||
DB894CC427A5490600684B74 /* BlurhashImageCacheService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB894CC327A5490600684B74 /* BlurhashImageCacheService.swift */; };
|
|
||||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */; };
|
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */; };
|
||||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52C25C13561002E6C99 /* DocumentStore.swift */; };
|
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52C25C13561002E6C99 /* DocumentStore.swift */; };
|
||||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52D25C13561002E6C99 /* AppContext.swift */; };
|
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52D25C13561002E6C99 /* AppContext.swift */; };
|
||||||
|
@ -532,6 +529,7 @@
|
||||||
DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; };
|
DBC6462826A1736300B0E31B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDE25BAA00100D1B89D /* Assets.xcassets */; };
|
||||||
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; };
|
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; };
|
||||||
DBC7A67C260DFADE00E57475 /* StatusPublishService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */; };
|
DBC7A67C260DFADE00E57475 /* StatusPublishService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */; };
|
||||||
|
DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCA0EBB282BB38A0029E2B0 /* PageboyNavigateable.swift */; };
|
||||||
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCBF3267CB070000F5B51 /* Decode85.swift */; };
|
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCBF3267CB070000F5B51 /* Decode85.swift */; };
|
||||||
DBCBCC0D2680B908000F5B51 /* HomeTimelinePreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCC0C2680B908000F5B51 /* HomeTimelinePreference.swift */; };
|
DBCBCC0D2680B908000F5B51 /* HomeTimelinePreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBCC0C2680B908000F5B51 /* HomeTimelinePreference.swift */; };
|
||||||
DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */; };
|
DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */; };
|
||||||
|
@ -1041,8 +1039,6 @@
|
||||||
DB519B15281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Intents.strings; sourceTree = "<group>"; };
|
DB519B15281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||||
DB519B16281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
DB519B16281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
DB519B17281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Intents.stringsdict; sourceTree = "<group>"; };
|
DB519B17281BCC2F00F0C99D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Intents.stringsdict; sourceTree = "<group>"; };
|
||||||
DB51D170262832380062B7A1 /* BlurHashDecode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
|
||||||
DB51D171262832380062B7A1 /* BlurHashEncode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
|
||||||
DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusFilterService.swift; sourceTree = "<group>"; };
|
DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusFilterService.swift; sourceTree = "<group>"; };
|
||||||
DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Poll.swift"; sourceTree = "<group>"; };
|
DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Poll.swift"; sourceTree = "<group>"; };
|
||||||
DB5B7294273112B100081888 /* FollowingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingListViewController.swift; sourceTree = "<group>"; };
|
DB5B7294273112B100081888 /* FollowingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingListViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1167,7 +1163,6 @@
|
||||||
DB852D1E26FB037800FC9D81 /* SidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewModel.swift; sourceTree = "<group>"; };
|
DB852D1E26FB037800FC9D81 /* SidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewModel.swift; sourceTree = "<group>"; };
|
||||||
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
DB87D4442609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionAppendEntryCollectionViewCell.swift; sourceTree = "<group>"; };
|
DB87D4502609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusPollOptionAppendEntryCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB894CC327A5490600684B74 /* BlurhashImageCacheService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurhashImageCacheService.swift; sourceTree = "<group>"; };
|
|
||||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mastodon.entitlements; sourceTree = "<group>"; };
|
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mastodon.entitlements; sourceTree = "<group>"; };
|
||||||
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewStateStore.swift; sourceTree = "<group>"; };
|
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewStateStore.swift; sourceTree = "<group>"; };
|
||||||
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentStore.swift; sourceTree = "<group>"; };
|
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentStore.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1296,6 +1291,7 @@
|
||||||
DBC6462226A1712000B0E31B /* ShareViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareViewModel.swift; sourceTree = "<group>"; };
|
DBC6462226A1712000B0E31B /* ShareViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareViewModel.swift; sourceTree = "<group>"; };
|
||||||
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; };
|
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; };
|
||||||
DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPublishService.swift; sourceTree = "<group>"; };
|
DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPublishService.swift; sourceTree = "<group>"; };
|
||||||
|
DBCA0EBB282BB38A0029E2B0 /* PageboyNavigateable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageboyNavigateable.swift; sourceTree = "<group>"; };
|
||||||
DBCBCBF3267CB070000F5B51 /* Decode85.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode85.swift; sourceTree = "<group>"; };
|
DBCBCBF3267CB070000F5B51 /* Decode85.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode85.swift; sourceTree = "<group>"; };
|
||||||
DBCBCC0C2680B908000F5B51 /* HomeTimelinePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelinePreference.swift; sourceTree = "<group>"; };
|
DBCBCC0C2680B908000F5B51 /* HomeTimelinePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelinePreference.swift; sourceTree = "<group>"; };
|
||||||
DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
DBCBED1626132DB500B49291 /* UserTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1665,8 +1661,6 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */,
|
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */,
|
||||||
DB51D170262832380062B7A1 /* BlurHashDecode.swift */,
|
|
||||||
DB51D171262832380062B7A1 /* BlurHashEncode.swift */,
|
|
||||||
DB6180EC26391C6C0018D199 /* TransitioningMath.swift */,
|
DB6180EC26391C6C0018D199 /* TransitioningMath.swift */,
|
||||||
DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */,
|
DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */,
|
||||||
DBF156E32702DB3F00EC00B7 /* HandleTapAction.swift */,
|
DBF156E32702DB3F00EC00B7 /* HandleTapAction.swift */,
|
||||||
|
@ -1692,7 +1686,6 @@
|
||||||
DB297B1A2679FAE200704C90 /* PlaceholderImageCacheService.swift */,
|
DB297B1A2679FAE200704C90 /* PlaceholderImageCacheService.swift */,
|
||||||
DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */,
|
DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */,
|
||||||
DB73BF42271192BB00781945 /* InstanceService.swift */,
|
DB73BF42271192BB00781945 /* InstanceService.swift */,
|
||||||
DB894CC327A5490600684B74 /* BlurhashImageCacheService.swift */,
|
|
||||||
);
|
);
|
||||||
path = Service;
|
path = Service;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1706,6 +1699,7 @@
|
||||||
DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */,
|
DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */,
|
||||||
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
|
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
|
||||||
DB1D84372657B275000346B3 /* SegmentedControlNavigateable.swift */,
|
DB1D84372657B275000346B3 /* SegmentedControlNavigateable.swift */,
|
||||||
|
DBCA0EBB282BB38A0029E2B0 /* PageboyNavigateable.swift */,
|
||||||
DB1D843326579931000346B3 /* TableViewControllerNavigateable.swift */,
|
DB1D843326579931000346B3 /* TableViewControllerNavigateable.swift */,
|
||||||
DB1D842D26552C4D000346B3 /* StatusTableViewControllerNavigateable.swift */,
|
DB1D842D26552C4D000346B3 /* StatusTableViewControllerNavigateable.swift */,
|
||||||
);
|
);
|
||||||
|
@ -4091,6 +4085,7 @@
|
||||||
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
|
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
|
||||||
DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */,
|
DBA94434265CBB5300C537E1 /* ProfileFieldSection.swift in Sources */,
|
||||||
DB336F28278D6EC70031E64B /* MastodonFieldContainer.swift in Sources */,
|
DB336F28278D6EC70031E64B /* MastodonFieldContainer.swift in Sources */,
|
||||||
|
DBCA0EBC282BB38A0029E2B0 /* PageboyNavigateable.swift in Sources */,
|
||||||
DBF156E42702DB3F00EC00B7 /* HandleTapAction.swift in Sources */,
|
DBF156E42702DB3F00EC00B7 /* HandleTapAction.swift in Sources */,
|
||||||
DB98EB4727B0DFAA0082E365 /* ReportViewModel+State.swift in Sources */,
|
DB98EB4727B0DFAA0082E365 /* ReportViewModel+State.swift in Sources */,
|
||||||
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
|
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
|
||||||
|
@ -4203,7 +4198,6 @@
|
||||||
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */,
|
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */,
|
||||||
DB36679D268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift in Sources */,
|
DB36679D268AB91B0027D07F /* ComposeStatusAttachmentTableViewCell.swift in Sources */,
|
||||||
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */,
|
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */,
|
||||||
DB894CC427A5490600684B74 /* BlurhashImageCacheService.swift in Sources */,
|
|
||||||
DBFEF07B26A6BCE8006D7ED1 /* APIService+Status+Publish.swift in Sources */,
|
DBFEF07B26A6BCE8006D7ED1 /* APIService+Status+Publish.swift in Sources */,
|
||||||
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */,
|
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */,
|
||||||
5B90C48526259BF10002E742 /* APIService+Subscriptions.swift in Sources */,
|
5B90C48526259BF10002E742 /* APIService+Subscriptions.swift in Sources */,
|
||||||
|
@ -4254,7 +4248,6 @@
|
||||||
DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */,
|
DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */,
|
||||||
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */,
|
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */,
|
||||||
DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */,
|
DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */,
|
||||||
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */,
|
|
||||||
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
|
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
|
||||||
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
|
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
|
||||||
DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */,
|
DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */,
|
||||||
|
@ -4312,7 +4305,6 @@
|
||||||
DB6180ED26391C6C0018D199 /* TransitioningMath.swift in Sources */,
|
DB6180ED26391C6C0018D199 /* TransitioningMath.swift in Sources */,
|
||||||
DB63F771279A858500455B82 /* Persistence+Notification.swift in Sources */,
|
DB63F771279A858500455B82 /* Persistence+Notification.swift in Sources */,
|
||||||
2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */,
|
2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */,
|
||||||
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */,
|
|
||||||
DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */,
|
DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */,
|
||||||
DB0617FD27855BFE0030EE79 /* ServerRuleItem.swift in Sources */,
|
DB0617FD27855BFE0030EE79 /* ServerRuleItem.swift in Sources */,
|
||||||
5BB04FD5262E7AFF0043BFF6 /* ReportViewController.swift in Sources */,
|
5BB04FD5262E7AFF0043BFF6 /* ReportViewController.swift in Sources */,
|
||||||
|
|
|
@ -16,10 +16,15 @@
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>27</integer>
|
<integer>27</integer>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>Mastodon - Profile.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>19</integer>
|
||||||
|
</dict>
|
||||||
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>6</integer>
|
<integer>5</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -109,7 +114,7 @@
|
||||||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>28</integer>
|
<integer>21</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -124,12 +129,12 @@
|
||||||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>27</integer>
|
<integer>20</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>26</integer>
|
<integer>22</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// PageboyNavigateable.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK on 2022-5-11.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Pageboy
|
||||||
|
import MastodonLocalization
|
||||||
|
|
||||||
|
typealias PageboyNavigateable = PageboyNavigateableCore & PageboyNavigateableRelay
|
||||||
|
|
||||||
|
protocol PageboyNavigateableCore: AnyObject {
|
||||||
|
var navigateablePageViewController: PageboyViewController { get }
|
||||||
|
var pageboyNavigateKeyCommands: [UIKeyCommand] { get }
|
||||||
|
|
||||||
|
func pageboyNavigateKeyCommandHandler(_ sender: UIKeyCommand)
|
||||||
|
func navigate(direction: PageboyNavigationDirection)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc protocol PageboyNavigateableRelay: AnyObject {
|
||||||
|
func pageboyNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PageboyNavigationDirection: String, CaseIterable {
|
||||||
|
case previous
|
||||||
|
case next
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .previous: return L10n.Common.Controls.Keyboard.SegmentedControl.previousSection
|
||||||
|
case .next: return L10n.Common.Controls.Keyboard.SegmentedControl.nextSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIKeyCommand input
|
||||||
|
var input: String {
|
||||||
|
switch self {
|
||||||
|
case .previous: return "["
|
||||||
|
case .next: return "]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var modifierFlags: UIKeyModifierFlags {
|
||||||
|
switch self {
|
||||||
|
case .previous: return [.shift, .command]
|
||||||
|
case .next: return [.shift, .command]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyList: Any {
|
||||||
|
return rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PageboyNavigateableCore where Self: PageboyNavigateableRelay {
|
||||||
|
var pageboyNavigateKeyCommands: [UIKeyCommand] {
|
||||||
|
PageboyNavigationDirection.allCases.map { direction in
|
||||||
|
UIKeyCommand(
|
||||||
|
title: direction.title,
|
||||||
|
image: nil,
|
||||||
|
action: #selector(Self.pageboyNavigateKeyCommandHandlerRelay(_:)),
|
||||||
|
input: direction.input,
|
||||||
|
modifierFlags: direction.modifierFlags,
|
||||||
|
propertyList: direction.propertyList,
|
||||||
|
alternates: [],
|
||||||
|
discoverabilityTitle: nil,
|
||||||
|
attributes: [],
|
||||||
|
state: .off
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageboyNavigateKeyCommandHandler(_ sender: UIKeyCommand) {
|
||||||
|
guard let rawValue = sender.propertyList as? String,
|
||||||
|
let direction = PageboyNavigationDirection(rawValue: rawValue) else { return }
|
||||||
|
navigate(direction: direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PageboyNavigateableCore {
|
||||||
|
func navigate(direction: PageboyNavigationDirection) {
|
||||||
|
switch direction {
|
||||||
|
case .previous:
|
||||||
|
navigateablePageViewController.scrollToPage(.previous, animated: true)
|
||||||
|
case .next:
|
||||||
|
navigateablePageViewController.scrollToPage(.next, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,7 +115,7 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid
|
||||||
|
|
||||||
guard let provider = self as? (DataSourceProvider & MediaPreviewableViewController) else { return }
|
guard let provider = self as? (DataSourceProvider & MediaPreviewableViewController) else { return }
|
||||||
guard let indexPathForSelectedRow = tableView.indexPathForSelectedRow,
|
guard let indexPathForSelectedRow = tableView.indexPathForSelectedRow,
|
||||||
let cell = tableView.cellForRow(at: indexPathForSelectedRow) as? StatusTableViewCell
|
let cell = tableView.cellForRow(at: indexPathForSelectedRow) as? StatusViewContainerTableViewCell
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
guard let mediaView = cell.statusView.mediaGridContainerView.mediaViews.first else { return }
|
guard let mediaView = cell.statusView.mediaGridContainerView.mediaViews.first else { return }
|
||||||
|
|
|
@ -152,3 +152,20 @@ extension DiscoveryCommunityViewController: ScrollViewContainer {
|
||||||
tableView
|
tableView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DiscoveryCommunityViewController {
|
||||||
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return navigationKeyCommands + statusNavigationKeyCommands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - StatusTableViewControllerNavigateable
|
||||||
|
extension DiscoveryCommunityViewController: StatusTableViewControllerNavigateable {
|
||||||
|
@objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
navigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func statusKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
statusKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import Tabman
|
import Tabman
|
||||||
|
import Pageboy
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
|
|
||||||
|
@ -126,3 +127,31 @@ extension DiscoveryViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - ScrollViewContainer
|
||||||
|
extension DiscoveryViewController: ScrollViewContainer {
|
||||||
|
var scrollView: UIScrollView? {
|
||||||
|
return (currentViewController as? ScrollViewContainer)?.scrollView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DiscoveryViewController {
|
||||||
|
|
||||||
|
public override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return pageboyNavigateKeyCommands
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - PageboyNavigateable
|
||||||
|
extension DiscoveryViewController: PageboyNavigateable {
|
||||||
|
|
||||||
|
var navigateablePageViewController: PageboyViewController {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func pageboyNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
pageboyNavigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -131,3 +131,98 @@ extension DiscoveryHashtagsViewController: ScrollViewContainer {
|
||||||
tableView
|
tableView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DiscoveryHashtagsViewController {
|
||||||
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return navigationKeyCommands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - TableViewControllerNavigateable
|
||||||
|
extension DiscoveryHashtagsViewController: TableViewControllerNavigateable {
|
||||||
|
@objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
navigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigate(direction: TableViewNavigationDirection) {
|
||||||
|
if let indexPathForSelectedRow = tableView.indexPathForSelectedRow {
|
||||||
|
// navigate up/down on the current selected item
|
||||||
|
navigateToTag(direction: direction, indexPath: indexPathForSelectedRow)
|
||||||
|
} else {
|
||||||
|
// set first visible item selected
|
||||||
|
navigateToFirstVisibleTag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func navigateToTag(direction: TableViewNavigationDirection, indexPath: IndexPath) {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
let items = diffableDataSource.snapshot().itemIdentifiers
|
||||||
|
guard let selectedItem = diffableDataSource.itemIdentifier(for: indexPath),
|
||||||
|
let selectedItemIndex = items.firstIndex(of: selectedItem) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _navigateToItem: DiscoveryItem? = {
|
||||||
|
var index = selectedItemIndex
|
||||||
|
while 0..<items.count ~= index {
|
||||||
|
index = {
|
||||||
|
switch direction {
|
||||||
|
case .up: return index - 1
|
||||||
|
case .down: return index + 1
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
guard 0..<items.count ~= index else { return nil }
|
||||||
|
let item = items[index]
|
||||||
|
|
||||||
|
guard Self.validNavigateableItem(item) else { continue }
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
guard let item = _navigateToItem, let indexPath = diffableDataSource.indexPath(for: item) else { return }
|
||||||
|
let scrollPosition: UITableView.ScrollPosition = overrideNavigationScrollPosition ?? Self.navigateScrollPosition(tableView: tableView, indexPath: indexPath)
|
||||||
|
tableView.selectRow(at: indexPath, animated: true, scrollPosition: scrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func navigateToFirstVisibleTag() {
|
||||||
|
guard let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows else { return }
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
|
||||||
|
var visibleItems: [DiscoveryItem] = indexPathsForVisibleRows.sorted().compactMap { indexPath in
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return nil }
|
||||||
|
guard Self.validNavigateableItem(item) else { return nil }
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
if indexPathsForVisibleRows.first?.row != 0, visibleItems.count > 1 {
|
||||||
|
// drop first when visible not the first cell of table
|
||||||
|
visibleItems.removeFirst()
|
||||||
|
}
|
||||||
|
guard let item = visibleItems.first, let indexPath = diffableDataSource.indexPath(for: item) else { return }
|
||||||
|
let scrollPosition: UITableView.ScrollPosition = overrideNavigationScrollPosition ?? Self.navigateScrollPosition(tableView: tableView, indexPath: indexPath)
|
||||||
|
tableView.selectRow(at: indexPath, animated: true, scrollPosition: scrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func validNavigateableItem(_ item: DiscoveryItem) -> Bool {
|
||||||
|
switch item {
|
||||||
|
case .hashtag:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func open() {
|
||||||
|
guard let indexPathForSelectedRow = tableView.indexPathForSelectedRow else { return }
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPathForSelectedRow) else { return }
|
||||||
|
|
||||||
|
guard case let .hashtag(tag) = item else { return }
|
||||||
|
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, hashtag: tag.name)
|
||||||
|
coordinator.present(
|
||||||
|
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
|
||||||
|
from: self,
|
||||||
|
transition: .show
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -131,3 +131,99 @@ extension DiscoveryNewsViewController: ScrollViewContainer {
|
||||||
tableView
|
tableView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DiscoveryNewsViewController {
|
||||||
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return navigationKeyCommands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DiscoveryNewsViewController: TableViewControllerNavigateable {
|
||||||
|
|
||||||
|
func navigate(direction: TableViewNavigationDirection) {
|
||||||
|
if let indexPathForSelectedRow = tableView.indexPathForSelectedRow {
|
||||||
|
// navigate up/down on the current selected item
|
||||||
|
navigateToLink(direction: direction, indexPath: indexPathForSelectedRow)
|
||||||
|
} else {
|
||||||
|
// set first visible item selected
|
||||||
|
navigateToFirstVisibleLink()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func navigateToLink(direction: TableViewNavigationDirection, indexPath: IndexPath) {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
let items = diffableDataSource.snapshot().itemIdentifiers
|
||||||
|
guard let selectedItem = diffableDataSource.itemIdentifier(for: indexPath),
|
||||||
|
let selectedItemIndex = items.firstIndex(of: selectedItem) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _navigateToItem: DiscoveryItem? = {
|
||||||
|
var index = selectedItemIndex
|
||||||
|
while 0..<items.count ~= index {
|
||||||
|
index = {
|
||||||
|
switch direction {
|
||||||
|
case .up: return index - 1
|
||||||
|
case .down: return index + 1
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
guard 0..<items.count ~= index else { return nil }
|
||||||
|
let item = items[index]
|
||||||
|
|
||||||
|
guard Self.validNavigateableItem(item) else { continue }
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
guard let item = _navigateToItem, let indexPath = diffableDataSource.indexPath(for: item) else { return }
|
||||||
|
let scrollPosition: UITableView.ScrollPosition = overrideNavigationScrollPosition ?? Self.navigateScrollPosition(tableView: tableView, indexPath: indexPath)
|
||||||
|
tableView.selectRow(at: indexPath, animated: true, scrollPosition: scrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func navigateToFirstVisibleLink() {
|
||||||
|
guard let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows else { return }
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
|
||||||
|
var visibleItems: [DiscoveryItem] = indexPathsForVisibleRows.sorted().compactMap { indexPath in
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return nil }
|
||||||
|
guard Self.validNavigateableItem(item) else { return nil }
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
if indexPathsForVisibleRows.first?.row != 0, visibleItems.count > 1 {
|
||||||
|
// drop first when visible not the first cell of table
|
||||||
|
visibleItems.removeFirst()
|
||||||
|
}
|
||||||
|
guard let item = visibleItems.first, let indexPath = diffableDataSource.indexPath(for: item) else { return }
|
||||||
|
let scrollPosition: UITableView.ScrollPosition = overrideNavigationScrollPosition ?? Self.navigateScrollPosition(tableView: tableView, indexPath: indexPath)
|
||||||
|
tableView.selectRow(at: indexPath, animated: true, scrollPosition: scrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func validNavigateableItem(_ item: DiscoveryItem) -> Bool {
|
||||||
|
switch item {
|
||||||
|
case .link:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func open() {
|
||||||
|
guard let indexPathForSelectedRow = tableView.indexPathForSelectedRow else { return }
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPathForSelectedRow) else { return }
|
||||||
|
|
||||||
|
guard case let .link(link) = item else { return }
|
||||||
|
guard let url = URL(string: link.url) else { return }
|
||||||
|
coordinator.present(
|
||||||
|
scene: .safari(url: url),
|
||||||
|
from: self,
|
||||||
|
transition: .safariPresent(animated: true, completion: nil)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
navigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -171,3 +171,20 @@ extension DiscoveryPostsViewController: DiscoveryIntroBannerViewDelegate {
|
||||||
UserDefaults.shared.discoveryIntroBannerNeedsHidden = true
|
UserDefaults.shared.discoveryIntroBannerNeedsHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DiscoveryPostsViewController {
|
||||||
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return navigationKeyCommands + statusNavigationKeyCommands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - StatusTableViewControllerNavigateable
|
||||||
|
extension DiscoveryPostsViewController: StatusTableViewControllerNavigateable {
|
||||||
|
@objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
navigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func statusKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
statusKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ extension HashtagTimelineViewController {
|
||||||
])
|
])
|
||||||
|
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
// tableView.prefetchDataSource = self
|
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel.setupDiffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
statusTableViewCellDelegate: self
|
statusTableViewCellDelegate: self
|
||||||
|
@ -158,27 +157,6 @@ extension HashtagTimelineViewController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - TableViewCellHeightCacheableContainer
|
|
||||||
//extension HashtagTimelineViewController: TableViewCellHeightCacheableContainer {
|
|
||||||
// var cellFrameCache: NSCache<NSNumber, NSValue> {
|
|
||||||
// return viewModel.cellFrameCache
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// MARK: - UIScrollViewDelegate
|
|
||||||
//extension HashtagTimelineViewController {
|
|
||||||
// func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
||||||
// aspectScrollViewDidScroll(scrollView)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//extension HashtagTimelineViewController: LoadMoreConfigurableTableViewContainer {
|
|
||||||
// typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
|
||||||
// typealias LoadingState = HashtagTimelineViewModel.LoadOldestState.Loading
|
|
||||||
// var loadMoreConfigurableTableView: UITableView { return tableView }
|
|
||||||
// var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.loadOldestStateMachine }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// MARK: - UITableViewDelegate
|
// MARK: - UITableViewDelegate
|
||||||
extension HashtagTimelineViewController: UITableViewDelegate, AutoGenerateTableViewDelegate {
|
extension HashtagTimelineViewController: UITableViewDelegate, AutoGenerateTableViewDelegate {
|
||||||
// sourcery:inline:HashtagTimelineViewController.AutoGenerateTableViewDelegate
|
// sourcery:inline:HashtagTimelineViewController.AutoGenerateTableViewDelegate
|
||||||
|
@ -206,82 +184,23 @@ extension HashtagTimelineViewController: UITableViewDelegate, AutoGenerateTableV
|
||||||
}
|
}
|
||||||
// sourcery:end
|
// sourcery:end
|
||||||
|
|
||||||
// func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
|
||||||
// return aspectTableView(tableView, estimatedHeightForRowAt: indexPath)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
|
||||||
// aspectTableView(tableView, willDisplay: cell, forRowAt: indexPath)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
|
||||||
// aspectTableView(tableView, didEndDisplaying: cell, forRowAt: indexPath)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
||||||
// aspectTableView(tableView, didSelectRowAt: indexPath)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
|
||||||
// return aspectTableView(tableView, contextMenuConfigurationForRowAt: indexPath, point: point)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
// return aspectTableView(tableView, previewForHighlightingContextMenuWithConfiguration: configuration)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
// return aspectTableView(tableView, previewForDismissingContextMenuWithConfiguration: configuration)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
|
||||||
// aspectTableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UITableViewDataSourcePrefetching
|
|
||||||
//extension HashtagTimelineViewController: UITableViewDataSourcePrefetching {
|
|
||||||
// func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
|
||||||
// aspectTableView(tableView, prefetchRowsAt: indexPaths)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// MARK: - StatusTableViewCellDelegate
|
// MARK: - StatusTableViewCellDelegate
|
||||||
extension HashtagTimelineViewController: StatusTableViewCellDelegate { }
|
extension HashtagTimelineViewController: StatusTableViewCellDelegate { }
|
||||||
|
|
||||||
// MARK: - AVPlayerViewControllerDelegate
|
extension HashtagTimelineViewController {
|
||||||
//extension HashtagTimelineViewController: AVPlayerViewControllerDelegate {
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
//
|
return navigationKeyCommands + statusNavigationKeyCommands
|
||||||
// func playerViewController(_ playerViewController: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
}
|
||||||
// aspectPlayerViewController(playerViewController, willBeginFullScreenPresentationWithAnimationCoordinator: coordinator)
|
}
|
||||||
// }
|
// MARK: - StatusTableViewControllerNavigateable
|
||||||
//
|
extension HashtagTimelineViewController: StatusTableViewControllerNavigateable {
|
||||||
// func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
@objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
// aspectPlayerViewController(playerViewController, willEndFullScreenPresentationWithAnimationCoordinator: coordinator)
|
navigateKeyCommandHandler(sender)
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
//}
|
@objc func statusKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
statusKeyCommandHandler(sender)
|
||||||
// MARK: - StatusTableViewCellDelegate
|
}
|
||||||
//extension HashtagTimelineViewController: StatusTableViewCellDelegate {
|
}
|
||||||
// weak var playerViewControllerDelegate: AVPlayerViewControllerDelegate? { return self }
|
|
||||||
// func parent() -> UIViewController { return self }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//extension HashtagTimelineViewController {
|
|
||||||
// override var keyCommands: [UIKeyCommand]? {
|
|
||||||
// return navigationKeyCommands + statusNavigationKeyCommands
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// MARK: - StatusTableViewControllerNavigateable
|
|
||||||
//extension HashtagTimelineViewController: StatusTableViewControllerNavigateable {
|
|
||||||
// @objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
|
||||||
// navigateKeyCommandHandler(sender)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @objc func statusKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
|
||||||
// statusKeyCommandHandler(sender)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
|
@ -13,8 +13,9 @@ import MetaTextKit
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonUI
|
import MastodonUI
|
||||||
import Tabman
|
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
import Tabman
|
||||||
|
import Pageboy
|
||||||
|
|
||||||
protocol ProfileViewModelEditable {
|
protocol ProfileViewModelEditable {
|
||||||
func isEdited() -> Bool
|
func isEdited() -> Bool
|
||||||
|
@ -1155,25 +1156,28 @@ extension ProfileViewController: ScrollViewContainer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension ProfileViewController {
|
extension ProfileViewController {
|
||||||
//
|
|
||||||
// override var keyCommands: [UIKeyCommand]? {
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
// if !viewModel.isEditing.value {
|
if !viewModel.isEditing.value {
|
||||||
// return segmentedControlNavigateKeyCommands
|
return pageboyNavigateKeyCommands
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// return nil
|
return nil
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
// MARK: - PageboyNavigateable
|
||||||
|
extension ProfileViewController: PageboyNavigateable {
|
||||||
|
|
||||||
|
var navigateablePageViewController: PageboyViewController {
|
||||||
|
return profileSegmentedViewController.pagingViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func pageboyNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
||||||
|
pageboyNavigateKeyCommandHandler(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - SegmentedControlNavigateable
|
|
||||||
//extension ProfileViewController: SegmentedControlNavigateable {
|
|
||||||
// var navigateableSegmentedControl: UISegmentedControl {
|
|
||||||
// profileHeaderViewController.pageSegmentedControl
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @objc func segmentedControlNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
|
||||||
// segmentedControlNavigateKeyCommandHandler(sender)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
|
@ -77,6 +77,16 @@ extension ContentSplitViewController {
|
||||||
mainTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
mainTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// response keyboard command tab switch
|
||||||
|
mainTabBarController.$currentTab
|
||||||
|
.sink { [weak self] tab in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if tab != self.currentSupplementaryTab {
|
||||||
|
self.currentSupplementaryTab = tab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
$currentSupplementaryTab
|
$currentSupplementaryTab
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink(receiveValue: { [weak self] tab in
|
.sink(receiveValue: { [weak self] tab in
|
||||||
|
|
|
@ -57,33 +57,7 @@ extension MediaView {
|
||||||
} // end switch
|
} // end switch
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if let previewURL = configuration.previewURL,
|
configuration.load()
|
||||||
let url = URL(string: previewURL)
|
|
||||||
{
|
|
||||||
let placeholder = UIImage.placeholder(color: .systemGray6)
|
|
||||||
let request = URLRequest(url: url)
|
|
||||||
ImageDownloader.default.download(request, completion: { response in
|
|
||||||
switch response.result {
|
|
||||||
case .success(let image):
|
|
||||||
configuration.previewImage = image
|
|
||||||
case .failure:
|
|
||||||
configuration.previewImage = placeholder
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if let assetURL = configuration.assetURL,
|
|
||||||
let blurhash = configuration.blurhash
|
|
||||||
{
|
|
||||||
AppContext.shared.blurhashImageCacheService.image(
|
|
||||||
blurhash: blurhash,
|
|
||||||
size: configuration.aspectRadio,
|
|
||||||
url: assetURL
|
|
||||||
)
|
|
||||||
.assign(to: \.blurhashImage, on: configuration)
|
|
||||||
.store(in: &configuration.blurhashImageDisposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.isReveal = status.isMediaSensitive ? status.isSensitiveToggled : true
|
configuration.isReveal = status.isMediaSensitive ? status.isSensitiveToggled : true
|
||||||
|
|
||||||
return configuration
|
return configuration
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import AlamofireImage
|
import AlamofireImage
|
||||||
|
import MastodonUI
|
||||||
|
|
||||||
class AppContext: ObservableObject {
|
class AppContext: ObservableObject {
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class AppContext: ObservableObject {
|
||||||
let photoLibraryService = PhotoLibraryService()
|
let photoLibraryService = PhotoLibraryService()
|
||||||
|
|
||||||
let placeholderImageCacheService = PlaceholderImageCacheService()
|
let placeholderImageCacheService = PlaceholderImageCacheService()
|
||||||
let blurhashImageCacheService = BlurhashImageCacheService()
|
let blurhashImageCacheService = BlurhashImageCacheService.shared
|
||||||
|
|
||||||
let documentStore: DocumentStore
|
let documentStore: DocumentStore
|
||||||
private var documentStoreSubscription: AnyCancellable!
|
private var documentStoreSubscription: AnyCancellable!
|
||||||
|
|
|
@ -10,6 +10,9 @@ import Combine
|
||||||
|
|
||||||
public final class BlurhashImageCacheService {
|
public final class BlurhashImageCacheService {
|
||||||
|
|
||||||
|
// MARK: - Singleton
|
||||||
|
public static let shared = BlurhashImageCacheService()
|
||||||
|
|
||||||
static let edgeMaxLength: CGFloat = 20
|
static let edgeMaxLength: CGFloat = 20
|
||||||
|
|
||||||
let cache = NSCache<Key, UIImage>()
|
let cache = NSCache<Key, UIImage>()
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
import Photos
|
import Photos
|
||||||
|
import AlamofireImage
|
||||||
|
|
||||||
extension MediaView {
|
extension MediaView {
|
||||||
public class Configuration: Hashable {
|
public class Configuration: Hashable {
|
||||||
|
@ -142,3 +143,37 @@ extension MediaView.Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension MediaView.Configuration {
|
||||||
|
|
||||||
|
public func load() {
|
||||||
|
if let previewURL = previewURL,
|
||||||
|
let url = URL(string: previewURL)
|
||||||
|
{
|
||||||
|
let placeholder = UIImage.placeholder(color: .systemGray6)
|
||||||
|
let request = URLRequest(url: url)
|
||||||
|
ImageDownloader.default.download(request, completion: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
switch response.result {
|
||||||
|
case .success(let image):
|
||||||
|
self.previewImage = image
|
||||||
|
case .failure:
|
||||||
|
self.previewImage = placeholder
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if let assetURL = assetURL,
|
||||||
|
let blurhash = blurhash
|
||||||
|
{
|
||||||
|
BlurhashImageCacheService.shared.image(
|
||||||
|
blurhash: blurhash,
|
||||||
|
size: aspectRadio,
|
||||||
|
url: assetURL
|
||||||
|
)
|
||||||
|
.assign(to: \.blurhashImage, on: self)
|
||||||
|
.store(in: &blurhashImageDisposeBag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import AlamofireImage
|
import AlamofireImage
|
||||||
|
|
|
@ -6,9 +6,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
import MastodonAsset
|
import MastodonAsset
|
||||||
|
|
||||||
public final class NewsView: UIView {
|
public final class NewsView: UIView {
|
||||||
|
|
||||||
|
static let imageViewWidth: CGFloat = 132
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
let container = UIStackView()
|
let container = UIStackView()
|
||||||
|
|
||||||
|
@ -44,10 +49,14 @@ public final class NewsView: UIView {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let imageView = MediaView()
|
let imageView = MediaView()
|
||||||
|
|
||||||
|
// let imageView = UIImageView()
|
||||||
|
// var imageViewMediaConfiguration: MediaView.Configuration?
|
||||||
|
|
||||||
public func prepareForReuse() {
|
public func prepareForReuse() {
|
||||||
providerFaviconImageView.tag = (0..<Int.max).randomElement() ?? -1
|
providerFaviconImageView.tag = (0..<Int.max).randomElement() ?? -1
|
||||||
imageView.prepareForReuse()
|
imageView.prepareForReuse()
|
||||||
|
disposeBag.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
|
@ -65,6 +74,7 @@ public final class NewsView: UIView {
|
||||||
extension NewsView {
|
extension NewsView {
|
||||||
private func _init() {
|
private func _init() {
|
||||||
// container: H - [ textContainer | imageView ]
|
// container: H - [ textContainer | imageView ]
|
||||||
|
container.distribution = .fill
|
||||||
container.axis = .horizontal
|
container.axis = .horizontal
|
||||||
container.spacing = 8
|
container.spacing = 8
|
||||||
container.translatesAutoresizingMaskIntoConstraints = false
|
container.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -121,6 +131,12 @@ extension NewsView {
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
imageView.widthAnchor.constraint(equalToConstant: 132).priority(.required - 1),
|
imageView.widthAnchor.constraint(equalToConstant: 132).priority(.required - 1),
|
||||||
])
|
])
|
||||||
|
imageView.setContentHuggingPriority(.defaultLow - 100, for: .vertical)
|
||||||
|
imageView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical)
|
||||||
|
imageView.imageView.setContentHuggingPriority(.defaultLow - 100, for: .vertical)
|
||||||
|
imageView.imageView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical)
|
||||||
|
imageView.blurhashImageView.setContentHuggingPriority(.defaultLow - 100, for: .vertical)
|
||||||
|
imageView.blurhashImageView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical)
|
||||||
imageView.isUserInteractionEnabled = false
|
imageView.isUserInteractionEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue