Compare commits

...

7 Commits

12 changed files with 246 additions and 16 deletions

View File

@ -238,6 +238,8 @@
D6B17255254F88B800128392 /* OppositeCollapseKeywordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */; }; D6B17255254F88B800128392 /* OppositeCollapseKeywordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */; };
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */; }; D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */; };
D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.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 */; }; D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; };
D6BC874521961F73006163F1 /* Gifu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; }; 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, ); }; }; 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 = "<group>"; }; D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OppositeCollapseKeywordsView.swift; sourceTree = "<group>"; };
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrayscalifier.swift; sourceTree = "<group>"; }; D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrayscalifier.swift; sourceTree = "<group>"; };
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = "<group>"; }; D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = "<group>"; };
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshableViewController.swift; sourceTree = "<group>"; };
D6B81F432560390300F6E31D /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = "<group>"; };
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = "<group>"; }; D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = "<group>"; };
D6BC874421961F73006163F1 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedTableViewController.swift; sourceTree = "<group>"; };
@ -1364,6 +1368,7 @@
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */, D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */, D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */, D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */,
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
); );
path = Utilities; path = Utilities;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1487,6 +1492,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D6F953EF21251A2900CF0F2B /* MastodonController.swift */, D6F953EF21251A2900CF0F2B /* MastodonController.swift */,
D6B81F432560390300F6E31D /* MenuController.swift */,
); );
path = Controllers; path = Controllers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1883,6 +1889,7 @@
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */, D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */, D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */,
D6E426812532814100C02E1C /* MaybeLazyStack.swift in Sources */, D6E426812532814100C02E1C /* MaybeLazyStack.swift in Sources */,
D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */,
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */, D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
D6A3BC852321F6C100FD64D5 /* AccountListTableViewController.swift in Sources */, D6A3BC852321F6C100FD64D5 /* AccountListTableViewController.swift in Sources */,
D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */, D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */,
@ -1976,6 +1983,7 @@
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */, 04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */,
D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */, D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */,
D670F8B62537DC890046588A /* EmojiPickerWrapper.swift in Sources */, D670F8B62537DC890046588A /* EmojiPickerWrapper.swift in Sources */,
D6B81F442560390300F6E31D /* MenuController.swift in Sources */,
D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */, D67C57AF21E28EAD00C3118B /* Array+Uniques.swift in Sources */,
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */, D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */,
D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */, D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */,

View File

@ -44,4 +44,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
AppDelegate.crashReporter.enable() AppDelegate.crashReporter.enable()
} }
override func buildMenu(with builder: UIMenuBuilder) {
super.buildMenu(with: builder)
if builder.system == .main {
MenuController.buildMainMenu(builder: builder)
}
}
} }

View File

@ -0,0 +1,109 @@
//
// MenuController.swift
// Tusker
//
// Created by Shadowfacts on 11/14/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
struct MenuController {
static func composeCommand() -> UIKeyCommand {
let selector: Selector
if #available(iOS 14.0, *) {
selector = #selector(MainSplitViewController.presentCompose)
} else {
selector = #selector(MainTabBarViewController.presentCompose)
}
return UIKeyCommand(title: "Compose", action: selector, input: "n", modifierFlags: .command)
}
static func refreshCommand(discoverabilityTitle: String?) -> UIKeyCommand {
return UIKeyCommand(title: "Refresh", action: #selector(RefreshableViewController.refresh), input: "r", modifierFlags: .command, discoverabilityTitle: discoverabilityTitle)
}
@available(iOS 14.0, *)
static func sidebarCommand(item: MainSidebarViewController.Item, command: String) -> UIKeyCommand {
let data: Any
if case let .tab(tab) = item {
data = tab.rawValue
} else if case .search = item {
data = "search"
} else if case .bookmarks = item {
data = "bookmarks"
} else {
fatalError()
}
return UIKeyCommand(
title: item.title,
image: UIImage(systemName: item.imageName!),
action: #selector(MainSplitViewController.handleSidebarItemCommand(_:)),
input: command,
modifierFlags: .command,
propertyList: data
)
}
@available(iOS 14.0, *)
static func sidebarItemKeyCommands() -> [UIKeyCommand] {
return [
sidebarCommand(item: .tab(.timelines), command: "1"),
sidebarCommand(item: .tab(.notifications), command: "2"),
sidebarCommand(item: .search, command: "3"),
sidebarCommand(item: .bookmarks, command: "4"),
sidebarCommand(item: .tab(.myProfile), command: "5"),
]
}
static func buildMainMenu(builder: UIMenuBuilder) {
builder.insertChild(buildFileMenu(), atStartOfMenu: .file)
builder.insertChild(buildViewMenu(), atStartOfMenu: .view)
}
private static func buildFileMenu() -> UIMenu {
return UIMenu(
title: "",
image: nil,
identifier: nil,
options: .displayInline,
children: [
composeCommand(),
refreshCommand(discoverabilityTitle: nil),
]
)
}
private static func buildViewMenu() -> UIMenu {
let children: [UIMenuElement]
if #available(iOS 14.0, *) {
children = sidebarItemKeyCommands()
} else {
children = []
}
return UIMenu(
title: "",
image: nil,
identifier: nil,
options: .displayInline,
children: children
)
}
}
extension MenuController {
@available(iOS 14.0, *)
class SidebarItem: NSObject, NSCopying {
let item: MainSidebarViewController.Item
init(item: MainSidebarViewController.Item) {
self.item = item
}
func copy(with zone: NSZone? = nil) -> Any {
return SidebarItem(item: self.item)
}
}
}

View File

@ -142,8 +142,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
func activateAccount(_ account: LocalData.UserAccountInfo, animated: Bool) { func activateAccount(_ account: LocalData.UserAccountInfo, animated: Bool) {
let oldIndex = LocalData.shared.accounts.firstIndex(where: { $0.id == LocalData.shared.mostRecentAccountID })!
let newIndex = LocalData.shared.accounts.firstIndex(of: account)!
LocalData.shared.setMostRecentAccount(account) LocalData.shared.setMostRecentAccount(account)
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account) window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account)
@ -151,7 +149,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let newRoot = createAppUI() let newRoot = createAppUI()
if let container = window?.rootViewController as? AccountSwitchingContainerViewController { if let container = window?.rootViewController as? AccountSwitchingContainerViewController {
let direction: AccountSwitchingContainerViewController.AnimationDirection let direction: AccountSwitchingContainerViewController.AnimationDirection
if animated { if animated,
let oldIndex = LocalData.shared.accounts.firstIndex(where: { $0.id == LocalData.shared.mostRecentAccountID }),
let newIndex = LocalData.shared.accounts.firstIndex(of: account) {
direction = newIndex > oldIndex ? .upwards : .downwards direction = newIndex > oldIndex ? .upwards : .downwards
} else { } else {
direction = .none direction = .none

View File

@ -59,7 +59,7 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
@State var visibilityButton: UIBarButtonItem? @State var visibilityButton: UIBarButtonItem?
func makeUIView(context: Context) -> UITextView { func makeUIView(context: Context) -> UITextView {
let textView = UITextView() let textView = WrappedTextView()
textView.delegate = context.coordinator textView.delegate = context.coordinator
textView.isEditable = true textView.isEditable = true
textView.backgroundColor = .clear textView.backgroundColor = .clear
@ -171,6 +171,35 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
return Coordinator(text: $text, uiState: uiState, didChange: textDidChange) return Coordinator(text: $text, uiState: uiState, didChange: textDidChange)
} }
class WrappedTextView: UITextView {
private let formattingActions = [#selector(toggleBoldface(_:)), #selector(toggleItalics(_:))]
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if formattingActions.contains(action) {
return Preferences.shared.statusContentType != .plain
}
return super.canPerformAction(action, withSender: sender)
}
override func toggleBoldface(_ sender: Any?) {
(delegate as! Coordinator).applyFormat(.bold)
}
override func toggleItalics(_ sender: Any?) {
(delegate as! Coordinator).applyFormat(.italics)
}
override func validate(_ command: UICommand) {
super.validate(command)
if formattingActions.contains(command.action),
Preferences.shared.statusContentType != .plain {
command.attributes.remove(.disabled)
}
}
}
class Coordinator: NSObject, UITextViewDelegate, ComposeAutocompleteHandler, ComposeTextViewCaretScrolling { class Coordinator: NSObject, UITextViewDelegate, ComposeAutocompleteHandler, ComposeTextViewCaretScrolling {
weak var textView: UITextView? weak var textView: UITextView?
var text: Binding<String> var text: Binding<String>
@ -192,9 +221,16 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
} }
@objc func formatButtonPressed(_ sender: UIBarButtonItem) { @objc func formatButtonPressed(_ sender: UIBarButtonItem) {
guard let textView = textView, textView.isFirstResponder else { return }
let format = StatusFormat.allCases[sender.tag] let format = StatusFormat.allCases[sender.tag]
guard let insertionResult = format.insertionResult else { return } applyFormat(format)
}
func applyFormat(_ format: StatusFormat) {
guard let textView = textView,
textView.isFirstResponder,
let insertionResult = format.insertionResult else {
return
}
let currentSelectedRange = textView.selectedRange let currentSelectedRange = textView.selectedRange
if currentSelectedRange.length == 0 { if currentSelectedRange.length == 0 {

View File

@ -78,7 +78,7 @@ class MainSidebarViewController: UIViewController {
let layout = UICollectionViewCompositionalLayout.list(using: .init(appearance: .sidebar)) let layout = UICollectionViewCompositionalLayout.list(using: .init(appearance: .sidebar))
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = .systemGroupedBackground collectionView.backgroundColor = .clear
collectionView.delegate = self collectionView.delegate = self
view.addSubview(collectionView) view.addSubview(collectionView)

View File

@ -50,12 +50,17 @@ class MainSplitViewController: UISplitViewController {
sidebar = MainSidebarViewController(mastodonController: mastodonController) sidebar = MainSidebarViewController(mastodonController: mastodonController)
sidebar.sidebarDelegate = self sidebar.sidebarDelegate = self
setViewController(sidebar, for: .primary) setViewController(sidebar, for: .primary)
primaryBackgroundStyle = .sidebar
setViewController(EnhancedNavigationViewController(), for: .secondary) setViewController(EnhancedNavigationViewController(), for: .secondary)
select(item: .tab(.timelines)) select(item: .tab(.timelines))
tabBarViewController = MainTabBarViewController(mastodonController: mastodonController) tabBarViewController = MainTabBarViewController(mastodonController: mastodonController)
setViewController(tabBarViewController, for: .compact) setViewController(tabBarViewController, for: .compact)
addKeyCommand(MenuController.composeCommand())
MenuController.sidebarItemKeyCommands().forEach(addKeyCommand(_:))
} }
func select(item: MainSidebarViewController.Item) { func select(item: MainSidebarViewController.Item) {
@ -72,6 +77,25 @@ class MainSplitViewController: UISplitViewController {
return new return new
} }
} }
@objc func handleSidebarItemCommand(_ command: UICommand) {
let item: MainSidebarViewController.Item
if let index = command.propertyList as? Int {
item = .tab(MainTabBarViewController.Tab(rawValue: index)!)
} else if let str = command.propertyList as? String {
if str == "search" {
item = .search
} else if str == "bookmarks" {
item = .bookmarks
} else {
fatalError()
}
} else {
fatalError()
}
sidebar.select(item: item, animated: false)
select(item: item)
}
} }
@ -315,7 +339,7 @@ fileprivate extension MainSidebarViewController.Item {
@available(iOS 14.0, *) @available(iOS 14.0, *)
extension MainSplitViewController: TuskerRootViewController { extension MainSplitViewController: TuskerRootViewController {
func presentCompose() { @objc func presentCompose() {
let vc = ComposeHostingController(mastodonController: mastodonController) let vc = ComposeHostingController(mastodonController: mastodonController)
let nav = EnhancedNavigationViewController(rootViewController: vc) let nav = EnhancedNavigationViewController(rootViewController: vc)
nav.presentationController?.delegate = vc nav.presentationController?.delegate = vc

View File

@ -156,7 +156,7 @@ extension MainTabBarViewController: FastAccountSwitcherViewControllerDelegate {
} }
extension MainTabBarViewController: TuskerRootViewController { extension MainTabBarViewController: TuskerRootViewController {
func presentCompose() { @objc func presentCompose() {
let vc = ComposeHostingController(mastodonController: mastodonController) let vc = ComposeHostingController(mastodonController: mastodonController)
let nav = EnhancedNavigationViewController(rootViewController: vc) let nav = EnhancedNavigationViewController(rootViewController: vc)
nav.presentationController?.delegate = vc nav.presentationController?.delegate = vc

View File

@ -38,8 +38,12 @@ class NotificationsTableViewController: EnhancedTableViewController {
super.init(style: .plain) super.init(style: .plain)
#if !targetEnvironment(macCatalyst)
self.refreshControl = UIRefreshControl() 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) { required init?(coder aDecoder: NSCoder) {
@ -256,7 +260,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
} }
} }
@objc func refreshNotifications(_ sender: Any) { @objc func refreshNotifications() {
guard let newer = newer else { return } guard let newer = newer else { return }
let request = Client.getNotifications(excludeTypes: excludedTypes, range: newer) let request = Client.getNotifications(excludeTypes: excludedTypes, range: newer)
@ -319,6 +323,12 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
} }
} }
extension NotificationsTableViewController: RefreshableViewController {
func refresh() {
refreshNotifications()
}
}
extension NotificationsTableViewController: BackgroundableViewController { extension NotificationsTableViewController: BackgroundableViewController {
func sceneDidEnterBackground() { func sceneDidEnterBackground() {
pruneOffscreenRows() pruneOffscreenRows()

View File

@ -44,8 +44,12 @@ class ProfileStatusesViewController: EnhancedTableViewController {
view.backgroundColor = .systemBackground view.backgroundColor = .systemBackground
#if !targetEnvironment(macCatalyst)
refreshControl = UIRefreshControl() 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.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 140 tableView.estimatedRowHeight = 140
@ -131,7 +135,7 @@ class ProfileStatusesViewController: EnhancedTableViewController {
// MARK: Interaction // MARK: Interaction
@objc func refreshStatuses(_ sender: UIRefreshControl) { @objc func refreshStatuses() {
guard let newer = newer else { return } guard let newer = newer else { return }
getStatuses(for: newer) { (response) in getStatuses(for: newer) { (response) in
@ -155,7 +159,7 @@ class ProfileStatusesViewController: EnhancedTableViewController {
self.tableView.insertRows(at: indexPaths, with: .none) self.tableView.insertRows(at: indexPaths, with: .none)
} }
self.refreshControl!.endRefreshing() self.refreshControl?.endRefreshing()
} }
} }
} }
@ -317,3 +321,9 @@ extension ProfileStatusesViewController: UITableViewDataSourcePrefetching {
} }
} }
} }
extension ProfileStatusesViewController: RefreshableViewController {
func refresh() {
refreshStatuses()
}
}

View File

@ -33,8 +33,12 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
title = timeline.title title = timeline.title
tabBarItem.image = timeline.tabBarImage tabBarItem.image = timeline.tabBarImage
#if !targetEnvironment(macCatalyst)
self.refreshControl = UIRefreshControl() 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) userActivity = UserActivityManager.showTimelineActivity(timeline: timeline)
} }
@ -213,7 +217,7 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
// MARK: - Interaction // MARK: - Interaction
@objc func refreshStatuses(_ sender: Any) { @objc func refreshStatuses() {
guard let newer = newer else { return } guard let newer = newer else { return }
let request = Client.getStatuses(timeline: timeline, range: newer) let request = Client.getStatuses(timeline: timeline, range: newer)
@ -288,6 +292,12 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching {
} }
} }
extension TimelineTableViewController: RefreshableViewController {
func refresh() {
refreshStatuses()
}
}
extension TimelineTableViewController: BackgroundableViewController { extension TimelineTableViewController: BackgroundableViewController {
func sceneDidEnterBackground() { func sceneDidEnterBackground() {
pruneOffscreenRows() pruneOffscreenRows()

View File

@ -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()
}