diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 1ed72b36..706d1c1a 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -238,6 +238,8 @@ D6B17255254F88B800128392 /* OppositeCollapseKeywordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */; }; D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */; }; D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */; }; + D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */; }; + D6B81F442560390300F6E31D /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B81F432560390300F6E31D /* MenuController.swift */; }; D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; }; D6BC874521961F73006163F1 /* Gifu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; }; D6BC874621961F73006163F1 /* Gifu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -585,6 +587,8 @@ D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OppositeCollapseKeywordsView.swift; sourceTree = ""; }; D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrayscalifier.swift; sourceTree = ""; }; D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = ""; }; + D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshableViewController.swift; sourceTree = ""; }; + D6B81F432560390300F6E31D /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = ""; }; D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = ""; }; D6BC874421961F73006163F1 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedTableViewController.swift; sourceTree = ""; }; @@ -1364,6 +1368,7 @@ D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */, D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */, D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */, + D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */, ); path = Utilities; sourceTree = ""; @@ -1487,6 +1492,7 @@ isa = PBXGroup; children = ( D6F953EF21251A2900CF0F2B /* MastodonController.swift */, + D6B81F432560390300F6E31D /* MenuController.swift */, ); path = Controllers; sourceTree = ""; @@ -1883,6 +1889,7 @@ D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */, D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */, D6E426812532814100C02E1C /* MaybeLazyStack.swift in Sources */, + D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */, D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */, D6A3BC852321F6C100FD64D5 /* AccountListTableViewController.swift in Sources */, D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */, @@ -1976,6 +1983,7 @@ 04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */, D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */, D670F8B62537DC890046588A /* EmojiPickerWrapper.swift in Sources */, + D6B81F442560390300F6E31D /* MenuController.swift in Sources */, D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */, D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */, D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */, diff --git a/Tusker/AppDelegate.swift b/Tusker/AppDelegate.swift index d22903ef..69284e99 100644 --- a/Tusker/AppDelegate.swift +++ b/Tusker/AppDelegate.swift @@ -44,4 +44,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { AppDelegate.crashReporter.enable() } + + override func buildMenu(with builder: UIMenuBuilder) { + super.buildMenu(with: builder) + + if builder.system == .main { + MenuController.buildMainMenu(builder: builder) + } + } } diff --git a/Tusker/Controllers/MenuController.swift b/Tusker/Controllers/MenuController.swift new file mode 100644 index 00000000..8502aac9 --- /dev/null +++ b/Tusker/Controllers/MenuController.swift @@ -0,0 +1,33 @@ +// +// MenuController.swift +// Tusker +// +// Created by Shadowfacts on 11/14/20. +// Copyright © 2020 Shadowfacts. All rights reserved. +// + +import UIKit + +struct MenuController { + + static func refreshCommand(discoverabilityTitle: String?) -> UIKeyCommand { + return UIKeyCommand(title: "Refresh", action: #selector(RefreshableViewController.refresh), input: "r", modifierFlags: .command, discoverabilityTitle: discoverabilityTitle) + } + + static func buildMainMenu(builder: UIMenuBuilder) { + builder.insertChild(buildViewMenu(), atEndOfMenu: .view) + } + + private static func buildViewMenu() -> UIMenu { + return UIMenu( + title: "", + image: nil, + identifier: nil, + options: .displayInline, + children: [ + refreshCommand(discoverabilityTitle: nil), + ] + ) + } + +} diff --git a/Tusker/Screens/Notifications/NotificationsTableViewController.swift b/Tusker/Screens/Notifications/NotificationsTableViewController.swift index 14f3f18a..d3b56778 100644 --- a/Tusker/Screens/Notifications/NotificationsTableViewController.swift +++ b/Tusker/Screens/Notifications/NotificationsTableViewController.swift @@ -40,8 +40,10 @@ class NotificationsTableViewController: EnhancedTableViewController { #if !targetEnvironment(macCatalyst) self.refreshControl = UIRefreshControl() - refreshControl!.addTarget(self, action: #selector(refreshNotifications(_:)), for: .valueChanged) + refreshControl!.addTarget(self, action: #selector(refreshNotifications), for: .valueChanged) #endif + + addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: NSLocalizedString("Refresh Notifications", comment: "refresh notifications command discoverability title"))) } required init?(coder aDecoder: NSCoder) { @@ -258,7 +260,7 @@ class NotificationsTableViewController: EnhancedTableViewController { } } - @objc func refreshNotifications(_ sender: Any) { + @objc func refreshNotifications() { guard let newer = newer else { return } let request = Client.getNotifications(excludeTypes: excludedTypes, range: newer) @@ -321,6 +323,12 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching { } } +extension NotificationsTableViewController: RefreshableViewController { + func refresh() { + refreshNotifications() + } +} + extension NotificationsTableViewController: BackgroundableViewController { func sceneDidEnterBackground() { pruneOffscreenRows() diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index 5ea28c46..a62ebbf9 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -46,9 +46,11 @@ class ProfileStatusesViewController: EnhancedTableViewController { #if !targetEnvironment(macCatalyst) refreshControl = UIRefreshControl() - refreshControl!.addTarget(self, action: #selector(refreshStatuses(_:)), for: .valueChanged) + refreshControl!.addTarget(self, action: #selector(refreshStatuses), for: .valueChanged) #endif + addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: NSLocalizedString("Refresh Statuses", comment: "refresh statuses command discoverability title"))) + tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 140 @@ -133,7 +135,7 @@ class ProfileStatusesViewController: EnhancedTableViewController { // MARK: Interaction - @objc func refreshStatuses(_ sender: UIRefreshControl) { + @objc func refreshStatuses() { guard let newer = newer else { return } getStatuses(for: newer) { (response) in @@ -157,7 +159,7 @@ class ProfileStatusesViewController: EnhancedTableViewController { self.tableView.insertRows(at: indexPaths, with: .none) } - self.refreshControl!.endRefreshing() + self.refreshControl?.endRefreshing() } } } @@ -319,3 +321,9 @@ extension ProfileStatusesViewController: UITableViewDataSourcePrefetching { } } } + +extension ProfileStatusesViewController: RefreshableViewController { + func refresh() { + refreshStatuses() + } +} diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index bf8bd44e..ea4bdaf2 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -35,9 +35,11 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC #if !targetEnvironment(macCatalyst) self.refreshControl = UIRefreshControl() - refreshControl!.addTarget(self, action: #selector(refreshStatuses(_:)), for: .valueChanged) + refreshControl!.addTarget(self, action: #selector(refreshStatuses), for: .valueChanged) #endif + addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: NSLocalizedString("Refresh Statuses", comment: "refresh statuses command discoverability title"))) + userActivity = UserActivityManager.showTimelineActivity(timeline: timeline) } @@ -215,7 +217,7 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC // MARK: - Interaction - @objc func refreshStatuses(_ sender: Any) { + @objc func refreshStatuses() { guard let newer = newer else { return } let request = Client.getStatuses(timeline: timeline, range: newer) @@ -290,6 +292,12 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching { } } +extension TimelineTableViewController: RefreshableViewController { + func refresh() { + refreshStatuses() + } +} + extension TimelineTableViewController: BackgroundableViewController { func sceneDidEnterBackground() { pruneOffscreenRows() diff --git a/Tusker/Screens/Utilities/RefreshableViewController.swift b/Tusker/Screens/Utilities/RefreshableViewController.swift new file mode 100644 index 00000000..de912216 --- /dev/null +++ b/Tusker/Screens/Utilities/RefreshableViewController.swift @@ -0,0 +1,15 @@ +// +// RefreshableViewController.swift +// Tusker +// +// Created by Shadowfacts on 11/14/20. +// Copyright © 2020 Shadowfacts. All rights reserved. +// + +import UIKit + +@objc protocol RefreshableViewController { + + func refresh() + +}