Refactor view controller creation/navigation into AppRouter
This commit is contained in:
parent
af8ceaeccb
commit
43242d4e04
|
@ -60,6 +60,7 @@
|
|||
D621544821682A9D0003D87D /* TabsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D621544721682A9D0003D87D /* TabsTableViewController.swift */; };
|
||||
D621544B21682AD30003D87D /* TabTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D621544A21682AD30003D87D /* TabTableViewCell.swift */; };
|
||||
D621544D21682AD90003D87D /* TabTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D621544C21682AD90003D87D /* TabTableViewCell.xib */; };
|
||||
D627FF74217BBC9700CC0648 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D627FF73217BBC9700CC0648 /* AppRouter.swift */; };
|
||||
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6289E83217B795D0003D1D7 /* LargeImageViewController.xib */; };
|
||||
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2421217AA7E1005076CC /* UserActivityManager.swift */; };
|
||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; };
|
||||
|
@ -247,6 +248,7 @@
|
|||
D621544721682A9D0003D87D /* TabsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewController.swift; sourceTree = "<group>"; };
|
||||
D621544A21682AD30003D87D /* TabTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D621544C21682AD90003D87D /* TabTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TabTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D627FF73217BBC9700CC0648 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||
D6289E83217B795D0003D1D7 /* LargeImageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LargeImageViewController.xib; sourceTree = "<group>"; };
|
||||
D62D2421217AA7E1005076CC /* UserActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityManager.swift; sourceTree = "<group>"; };
|
||||
D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = "<group>"; };
|
||||
|
@ -743,6 +745,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
||||
D627FF73217BBC9700CC0648 /* AppRouter.swift */,
|
||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */,
|
||||
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
|
||||
|
@ -1137,6 +1140,7 @@
|
|||
D6C693CA2161253F007D6A6D /* SilentActionPermissionsTableViewController.swift in Sources */,
|
||||
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */,
|
||||
D641C777213CAA9E004B4513 /* ActionNotificationTableViewCell.swift in Sources */,
|
||||
D627FF74217BBC9700CC0648 /* AppRouter.swift in Sources */,
|
||||
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
||||
D663626821360E2C00C9CBA2 /* PreferencesTableViewController.swift in Sources */,
|
||||
|
|
|
@ -12,6 +12,7 @@ import UIKit
|
|||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
var router: AppRouter!
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
|
@ -66,7 +67,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
MastodonController.getOwnAccount()
|
||||
MastodonController.getOwnInstance()
|
||||
|
||||
window!.rootViewController = MainTabBarViewController()
|
||||
let tabBarController = MainTabBarViewController()
|
||||
window!.rootViewController = tabBarController
|
||||
router = tabBarController.router
|
||||
}
|
||||
|
||||
func showOnboardingUI() {
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// AppRouter.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 10/20/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import SafariServices
|
||||
|
||||
class AppRouter {
|
||||
|
||||
let rootViewController: UIViewController
|
||||
var currentViewController: UIViewController {
|
||||
return getContentViewController(from: rootViewController)
|
||||
}
|
||||
|
||||
init(root: UIViewController) {
|
||||
self.rootViewController = root
|
||||
}
|
||||
|
||||
private func getContentViewController(from vc: UIViewController) -> UIViewController {
|
||||
if let vc = vc as? UITabBarController,
|
||||
let selected = vc.selectedViewController {
|
||||
return getContentViewController(from: selected)
|
||||
} else if let vc = vc as? UINavigationController,
|
||||
let top = vc.topViewController {
|
||||
return getContentViewController(from: top)
|
||||
} else {
|
||||
return vc
|
||||
}
|
||||
}
|
||||
|
||||
func present(_ vc: UIViewController, animated: Bool) {
|
||||
if currentViewController.presentingViewController != nil {
|
||||
currentViewController.dismiss(animated: animated) {
|
||||
self.present(vc, animated: animated)
|
||||
}
|
||||
}
|
||||
currentViewController.present(vc, animated: animated)
|
||||
}
|
||||
|
||||
func push(_ vc: UIViewController, animated: Bool) {
|
||||
if currentViewController.presentingViewController != nil {
|
||||
currentViewController.dismiss(animated: animated) {
|
||||
self.push(vc, animated: animated)
|
||||
}
|
||||
}
|
||||
guard let nav = currentViewController.navigationController else { fatalError("Can't push view controller while not in navigation controller") }
|
||||
nav.pushViewController(vc, animated: animated)
|
||||
}
|
||||
|
||||
func profile(for accountID: String) -> ProfileTableViewController {
|
||||
return ProfileTableViewController(accountID: accountID, router: self)
|
||||
}
|
||||
|
||||
func profile(for mention: Mention) -> ProfileTableViewController {
|
||||
return ProfileTableViewController(accountID: mention.id, router: self)
|
||||
}
|
||||
|
||||
func timeline(for timeline: Timeline) -> TimelineTableViewController {
|
||||
return TimelineTableViewController(for: timeline, router: self)
|
||||
}
|
||||
|
||||
func timeline(tag: Hashtag) -> TimelineTableViewController {
|
||||
return timeline(for: .tag(hashtag: tag.name))
|
||||
}
|
||||
|
||||
func safariViewController(for url: URL) -> SFSafariViewController {
|
||||
return SFSafariViewController(url: url)
|
||||
}
|
||||
|
||||
func conversation(for statusID: String) -> ConversationTableViewController {
|
||||
return ConversationTableViewController(for: statusID, router: self)
|
||||
}
|
||||
|
||||
func compose(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil) -> ComposeViewController {
|
||||
return ComposeViewController(inReplyTo: inReplyToID, mentioningAcct: mentioningAcct, text: text, router: self)
|
||||
}
|
||||
|
||||
func largeImage(_ image: UIImage, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, transitioningDelegate: UIViewControllerTransitioningDelegate?) -> LargeImageViewController {
|
||||
let vc = LargeImageViewController(image: image, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius, router: self)
|
||||
vc.transitioningDelegate = transitioningDelegate
|
||||
return vc
|
||||
}
|
||||
|
||||
func moreOptions(forStatus statusID: String) -> UIAlertController {
|
||||
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
||||
|
||||
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
if let url = status.url {
|
||||
alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { (_) in
|
||||
let vc = SFSafariViewController(url: url)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (_) in
|
||||
UIPasteboard.general.url = url
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Share...", style: .default, handler: { (_) in
|
||||
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
||||
return alert
|
||||
}
|
||||
|
||||
func moreOptions(forURL url: URL) -> UIAlertController {
|
||||
let alert = UIAlertController(title: nil, message: url.absoluteString, preferredStyle: .actionSheet)
|
||||
alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { (_) in
|
||||
let vc = SFSafariViewController(url: url)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (_) in
|
||||
UIPasteboard.general.url = url
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Share...", style: .default, handler: { (_) in
|
||||
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
||||
return alert
|
||||
}
|
||||
|
||||
}
|
|
@ -7,14 +7,6 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import SafariServices
|
||||
|
||||
extension LargeImageViewControllerDelegate where Self: UIViewController {
|
||||
func closeLargeImage() {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController: UIViewControllerTransitioningDelegate {
|
||||
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
|
|
|
@ -12,6 +12,8 @@ import Intents
|
|||
|
||||
class ComposeViewController: UIViewController {
|
||||
|
||||
let router: AppRouter
|
||||
|
||||
@IBOutlet weak var scrollView: UIScrollView!
|
||||
@IBOutlet weak var inReplyToContainerView: UIView!
|
||||
@IBOutlet weak var inReplyToAvatarImageView: UIImageView!
|
||||
|
@ -51,11 +53,14 @@ class ComposeViewController: UIViewController {
|
|||
|
||||
var status: Status?
|
||||
|
||||
init(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil) {
|
||||
super.init(nibName: "ComposeViewController", bundle: nil)
|
||||
init(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil, router: AppRouter) {
|
||||
self.inReplyToID = inReplyToID
|
||||
self.mentioningAcct = mentioningAcct
|
||||
self.text = text
|
||||
self.router = router
|
||||
|
||||
super.init(nibName: "ComposeViewController", bundle: nil)
|
||||
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelPressed))
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,9 @@ import Pachyderm
|
|||
|
||||
class ConversationTableViewController: UITableViewController {
|
||||
|
||||
var mainStatusID: String!
|
||||
let router: AppRouter
|
||||
|
||||
var mainStatusID: String!
|
||||
var statusIDs: [String] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
|
@ -21,9 +22,11 @@ class ConversationTableViewController: UITableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
init(for mainStatusID: String) {
|
||||
super.init(style: .plain)
|
||||
init(for mainStatusID: String, router: AppRouter) {
|
||||
self.mainStatusID = mainStatusID
|
||||
self.router = router
|
||||
|
||||
super.init(style: .plain)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
@ -74,7 +77,7 @@ class ConversationTableViewController: UITableViewController {
|
|||
if let status = MastodonCache.status(for: mainStatusID),
|
||||
let url = status.url {
|
||||
actions.append(UIPreviewAction(title: "Open in Safari", style: .default, handler: { (_, _) in
|
||||
let vc = self.viewController(forURL: url)
|
||||
let vc = self.router.safariViewController(for: url)
|
||||
UIApplication.shared.delegate!.window!!.rootViewController!.present(vc, animated: true)
|
||||
}))
|
||||
actions.append(UIPreviewAction(title: "Share", style: .default, handler: { (_, _) in
|
||||
|
@ -145,4 +148,3 @@ class ConversationTableViewController: UITableViewController {
|
|||
}
|
||||
|
||||
extension ConversationTableViewController: StatusTableViewCellDelegate {}
|
||||
extension ConversationTableViewController: LargeImageViewControllerDelegate {}
|
||||
|
|
|
@ -9,13 +9,9 @@
|
|||
import UIKit
|
||||
import Photos
|
||||
|
||||
protocol LargeImageViewControllerDelegate {
|
||||
func closeLargeImage()
|
||||
}
|
||||
|
||||
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
||||
|
||||
var delegate: LargeImageViewControllerDelegate?
|
||||
let router: AppRouter
|
||||
|
||||
var originFrame: CGRect?
|
||||
var originCornerRadius: CGFloat?
|
||||
|
@ -64,22 +60,14 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
|||
return true
|
||||
}
|
||||
|
||||
init(image: UIImage, description: String?, sourceView: UIView, sourceViewController: UIViewController) {
|
||||
super.init(nibName: "LargeImageViewController", bundle: nil)
|
||||
init(image: UIImage, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, router: AppRouter) {
|
||||
self.router = router
|
||||
self.image = image
|
||||
self.imageDescription = description
|
||||
var frame = sourceView.convert(sourceView.bounds, to: sourceViewController.view)
|
||||
if let scrollView = sourceViewController.view as? UIScrollView {
|
||||
let scale = scrollView.zoomScale
|
||||
let width = frame.width * scale
|
||||
let height = frame.height * scale
|
||||
let x = frame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
||||
let y = frame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
||||
frame = CGRect(x: x, y: y, width: width, height: height)
|
||||
}
|
||||
self.originFrame = frame
|
||||
self.originCornerRadius = sourceView.layer.cornerRadius
|
||||
self.transitioningDelegate = sourceViewController
|
||||
self.originFrame = sourceFrame
|
||||
self.originCornerRadius = sourceCornerRadius
|
||||
|
||||
super.init(nibName: "LargeImageViewController", bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
@ -206,7 +194,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
|||
}
|
||||
|
||||
@IBAction func closeButtonPressed(_ sender: Any) {
|
||||
delegate?.closeLargeImage()
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func downloadPressed(_ sender: Any) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import UIKit
|
|||
|
||||
class MainTabBarViewController: UITabBarController {
|
||||
|
||||
lazy var router = AppRouter(root: self)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
@ -31,20 +33,20 @@ class MainTabBarViewController: UITabBarController {
|
|||
func createVC(for tab: Tab) -> UIViewController {
|
||||
switch tab {
|
||||
case .home:
|
||||
return TimelineTableViewController(for: .home)
|
||||
return TimelineTableViewController(for: .home, router: router)
|
||||
case .federated:
|
||||
return TimelineTableViewController(for: .public(local: false))
|
||||
return TimelineTableViewController(for: .public(local: false), router: router)
|
||||
case .local:
|
||||
return TimelineTableViewController(for: .public(local: true))
|
||||
return TimelineTableViewController(for: .public(local: true), router: router)
|
||||
case .myProfile:
|
||||
let myProfile = ProfileTableViewController(accountID: nil)
|
||||
let myProfile = ProfileTableViewController(accountID: nil, router: router)
|
||||
myProfile.title = "My Profile"
|
||||
MastodonController.getOwnAccount { (account) in
|
||||
myProfile.accountID = account.id
|
||||
}
|
||||
return myProfile
|
||||
case .notifications:
|
||||
return embedInNavigationController(NotificationsTableViewController())
|
||||
return NotificationsTableViewController(router: router)
|
||||
case .preferences:
|
||||
return PreferencesTableViewController.create()
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import Pachyderm
|
|||
|
||||
class NotificationsTableViewController: UITableViewController {
|
||||
|
||||
let router: AppRouter
|
||||
|
||||
var notifications: [Pachyderm.Notification] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
|
@ -22,8 +24,11 @@ class NotificationsTableViewController: UITableViewController {
|
|||
var newer: RequestRange?
|
||||
var older: RequestRange?
|
||||
|
||||
init() {
|
||||
init(router: AppRouter) {
|
||||
self.router = router
|
||||
|
||||
super.init(style: .plain)
|
||||
|
||||
self.title = "Notifications"
|
||||
self.refreshControl = UIRefreshControl()
|
||||
refreshControl!.addTarget(self, action: #selector(refreshNotifications(_:)), for: .valueChanged)
|
||||
|
@ -149,4 +154,3 @@ class NotificationsTableViewController: UITableViewController {
|
|||
}
|
||||
|
||||
extension NotificationsTableViewController: StatusTableViewCellDelegate {}
|
||||
extension NotificationsTableViewController: LargeImageViewControllerDelegate {}
|
||||
|
|
|
@ -12,6 +12,8 @@ import SafariServices
|
|||
|
||||
class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
|
||||
|
||||
let router: AppRouter
|
||||
|
||||
var accountID: String! {
|
||||
didSet {
|
||||
if shouldLoadOnAccountIDSet {
|
||||
|
@ -36,9 +38,12 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
|
|||
var shouldLoadOnAccountIDSet = false
|
||||
var loadingVC: LoadingViewController? = nil
|
||||
|
||||
init(accountID: String?) {
|
||||
super.init(style: .plain)
|
||||
init(accountID: String?, router: AppRouter) {
|
||||
self.accountID = accountID
|
||||
self.router = router
|
||||
|
||||
super.init(style: .plain)
|
||||
|
||||
self.refreshControl = UIRefreshControl()
|
||||
refreshControl!.addTarget(self, action: #selector(refreshStatuses(_:)), for: .valueChanged)
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(composePressed(_:)))
|
||||
|
@ -107,7 +112,7 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
|
|||
var actions = [UIPreviewActionItem]()
|
||||
if let account = MastodonCache.account(for: accountID) {
|
||||
actions.append(UIPreviewAction(title: "Open in Safari", style: .default, handler: { (_, _) in
|
||||
let vc = self.viewController(forURL: account.url)
|
||||
let vc = self.router.safariViewController(for: account.url)
|
||||
UIApplication.shared.delegate!.window!!.rootViewController!.present(vc, animated: true)
|
||||
}))
|
||||
actions.append(UIPreviewAction(title: "Share", style: .default, handler: { (_, _) in
|
||||
|
@ -115,7 +120,7 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
|
|||
UIApplication.shared.delegate!.window!!.rootViewController!.present(vc, animated: true)
|
||||
}))
|
||||
actions.append(UIPreviewAction(title: "Send Message", style: .default, handler: { (_, _) in
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct))
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct, router: self.router))
|
||||
UIApplication.shared.delegate!.window!!.rootViewController!.present(vc, animated: true)
|
||||
}))
|
||||
}
|
||||
|
@ -148,7 +153,7 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive {
|
|||
|
||||
func sendMessageMentioning() {
|
||||
guard let account = MastodonCache.account(for: accountID) else { fatalError("Missing cached account \(accountID!)") }
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct))
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct, router: router))
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
|
@ -279,4 +284,3 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
extension ProfileTableViewController: LargeImageViewControllerDelegate {}
|
||||
|
|
|
@ -11,6 +11,8 @@ import Pachyderm
|
|||
|
||||
class TimelineTableViewController: UITableViewController {
|
||||
|
||||
let router: AppRouter
|
||||
|
||||
lazy var favoriteActionImage: UIImage = UIGraphicsImageRenderer(size: CGSize(width: 30 * 137/131, height: 30)).image { _ in
|
||||
UIImage(named: "Favorite")!.draw(in: CGRect(x: 0, y: 0, width: 30 * 137/131, height: 30))
|
||||
}
|
||||
|
@ -37,9 +39,11 @@ class TimelineTableViewController: UITableViewController {
|
|||
var newer: RequestRange?
|
||||
var older: RequestRange?
|
||||
|
||||
init(for timeline: Timeline) {
|
||||
super.init(style: .plain)
|
||||
init(for timeline: Timeline, router: AppRouter) {
|
||||
self.timeline = timeline
|
||||
self.router = router
|
||||
|
||||
super.init(style: .plain)
|
||||
|
||||
switch timeline {
|
||||
case .home:
|
||||
|
@ -168,4 +172,3 @@ class TimelineTableViewController: UITableViewController {
|
|||
}
|
||||
|
||||
extension TimelineTableViewController: StatusTableViewCellDelegate {}
|
||||
extension TimelineTableViewController: LargeImageViewControllerDelegate {}
|
||||
|
|
|
@ -12,15 +12,9 @@ import Pachyderm
|
|||
class UserActivityManager {
|
||||
|
||||
// MARK: - Utils
|
||||
private static func presentModally(_ vc: UIViewController, animated: Bool, completion: (() -> Void)? = nil) {
|
||||
UIApplication.shared.keyWindow!.rootViewController!.present(vc, animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
private static func presentNav(_ vc: UIViewController, animated: Bool) {
|
||||
let tabBarController = UIApplication.shared.keyWindow!.rootViewController! as! UITabBarController
|
||||
let navController = tabBarController.selectedViewController as! UINavigationController
|
||||
navController.pushViewController(vc, animated: animated)
|
||||
}
|
||||
private static var router: AppRouter = {
|
||||
return (UIApplication.shared.delegate as! AppDelegate).router
|
||||
}()
|
||||
|
||||
// MARK: - New Post
|
||||
static func newPostActivity(mentioning: Account? = nil) -> NSUserActivity {
|
||||
|
@ -41,7 +35,7 @@ class UserActivityManager {
|
|||
static func handleNewPost(activity: NSUserActivity) {
|
||||
// TODO: check not currently showing compose screen
|
||||
let mentioning = activity.userInfo?["mentioning"] as? String
|
||||
presentModally(UINavigationController(rootViewController: ComposeViewController(mentioningAcct: mentioning)), animated: true)
|
||||
router.present(router.compose(mentioningAcct: mentioning), animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Check Notifications
|
||||
|
|
|
@ -11,33 +11,26 @@ import SafariServices
|
|||
import Pachyderm
|
||||
|
||||
protocol TuskerNavigationDelegate {
|
||||
func viewController(forAccount accountID: String) -> UIViewController
|
||||
|
||||
var router: AppRouter { get }
|
||||
|
||||
func selected(account accountID: String)
|
||||
|
||||
func viewController(forMention mention: Mention) -> UIViewController
|
||||
|
||||
func selected(mention: Mention)
|
||||
|
||||
func viewController(forTag tag: Hashtag) -> UIViewController
|
||||
|
||||
func selected(tag: Hashtag)
|
||||
|
||||
func viewController(forURL url: URL) -> UIViewController
|
||||
|
||||
func selected(url: URL)
|
||||
|
||||
func viewController(forStatus statusID: String) -> UIViewController
|
||||
|
||||
func selected(status statusID: String)
|
||||
|
||||
func compose()
|
||||
|
||||
func reply(to statusID: String)
|
||||
|
||||
func viewController(forImage image: UIImage, description: String?, animatingFrom originView: UIView) -> UIViewController
|
||||
func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController
|
||||
|
||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView)
|
||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView)
|
||||
|
||||
func showMoreOptions(forStatus statusID: String)
|
||||
|
||||
|
@ -46,10 +39,6 @@ protocol TuskerNavigationDelegate {
|
|||
|
||||
extension TuskerNavigationDelegate where Self: UIViewController {
|
||||
|
||||
func viewController(forAccount accountID: String) -> UIViewController {
|
||||
return ProfileTableViewController(accountID: accountID)
|
||||
}
|
||||
|
||||
func selected(account accountID: String) {
|
||||
// don't open if the account is the same as the current one
|
||||
if let profileController = self as? ProfileTableViewController,
|
||||
|
@ -57,49 +46,19 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
|||
return
|
||||
}
|
||||
|
||||
guard let navigationController = navigationController else {
|
||||
fatalError("Can't show profile VC when not in navigation controller")
|
||||
}
|
||||
let vc = viewController(forAccount: accountID)
|
||||
navigationController.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
func viewController(forMention mention: Mention) -> UIViewController {
|
||||
return ProfileTableViewController(accountID: mention.id)
|
||||
router.push(router.profile(for: accountID), animated: true)
|
||||
}
|
||||
|
||||
func selected(mention: Mention) {
|
||||
guard let navigationController = navigationController else {
|
||||
fatalError("Can't show profile VC from mention when not in navigation controller")
|
||||
}
|
||||
let vc = viewController(forMention: mention)
|
||||
navigationController.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
func viewController(forTag tag: Hashtag) -> UIViewController {
|
||||
let timeline = Timeline.tag(hashtag: tag.name)
|
||||
return TimelineTableViewController(for: timeline)
|
||||
router.push(router.profile(for: mention), animated: true)
|
||||
}
|
||||
|
||||
func selected(tag: Hashtag) {
|
||||
guard let navigationController = navigationController else {
|
||||
fatalError("Can't show hashtag timeline when not in navigation controller")
|
||||
}
|
||||
let vc = viewController(forTag: tag)
|
||||
navigationController.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
func viewController(forURL url: URL) -> UIViewController {
|
||||
return SFSafariViewController(url: url)
|
||||
router.push(router.timeline(tag: tag), animated: true)
|
||||
}
|
||||
|
||||
func selected(url: URL) {
|
||||
let vc = viewController(forURL: url)
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
func viewController(forStatus statusID: String) -> UIViewController {
|
||||
return ConversationTableViewController(for: statusID)
|
||||
router.present(router.safariViewController(for: url), animated: true)
|
||||
}
|
||||
|
||||
func selected(status statusID: String) {
|
||||
|
@ -109,73 +68,43 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
|||
return
|
||||
}
|
||||
|
||||
guard let navigationController = navigationController else {
|
||||
fatalError("Can't show conversation VC when not in navigation controller")
|
||||
}
|
||||
let vc = viewController(forStatus: statusID)
|
||||
navigationController.pushViewController(vc, animated: true)
|
||||
router.push(router.conversation(for: statusID), animated: true)
|
||||
}
|
||||
|
||||
func compose() {
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController())
|
||||
present(vc, animated: true)
|
||||
let vc: UINavigationController = UINavigationController(rootViewController: router.compose())
|
||||
router.present(vc, animated: true)
|
||||
}
|
||||
|
||||
func reply(to statusID: String) {
|
||||
let vc = UINavigationController(rootViewController: ComposeViewController(inReplyTo: statusID))
|
||||
present(vc, animated: true)
|
||||
let vc = UINavigationController(rootViewController: router.compose(inReplyTo: statusID))
|
||||
router.present(vc, animated: true)
|
||||
}
|
||||
|
||||
func viewController(forImage image: UIImage, description: String?, animatingFrom originView: UIView) -> UIViewController {
|
||||
guard let self = self as? UIViewController & LargeImageViewControllerDelegate else {
|
||||
fatalError("Can't create large image view controller unless self is LargeImageViewControllerDelegate")
|
||||
func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController {
|
||||
var sourceFrame = sourceView.convert(sourceView.bounds, to: view)
|
||||
if let scrollView = view as? UIScrollView {
|
||||
let scale = scrollView.zoomScale
|
||||
let width = sourceFrame.width * scale
|
||||
let height = sourceFrame.height * scale
|
||||
let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX
|
||||
let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
|
||||
sourceFrame = CGRect(x: x, y: y, width: width, height: height)
|
||||
}
|
||||
let vc = LargeImageViewController(image: image, description: description, sourceView: originView, sourceViewController: self)
|
||||
vc.delegate = self
|
||||
return vc
|
||||
let sourceCornerRadius = sourceView.layer.cornerRadius
|
||||
return router.largeImage(image, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius, transitioningDelegate: self)
|
||||
}
|
||||
|
||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView) {
|
||||
let vc = viewController(forImage: image, description: description, animatingFrom: originView)
|
||||
present(vc, animated: true)
|
||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView) {
|
||||
router.present(largeImage(image, description: description, sourceView: sourceView), animated: true)
|
||||
}
|
||||
|
||||
func showMoreOptions(forStatus statusID: String) {
|
||||
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
||||
|
||||
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
if let url = status.url {
|
||||
alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { (_) in
|
||||
let vc = SFSafariViewController(url: url)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (_) in
|
||||
UIPasteboard.general.url = url
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Share...", style: .default, handler: { (_) in
|
||||
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
||||
present(alert, animated: true)
|
||||
router.present(router.moreOptions(forStatus: statusID), animated: true)
|
||||
}
|
||||
|
||||
func showMoreOptions(forURL url: URL) {
|
||||
let alert = UIAlertController(title: nil, message: url.absoluteString, preferredStyle: .actionSheet)
|
||||
alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { (_) in
|
||||
let vc = SFSafariViewController(url: url)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (_) in
|
||||
UIPasteboard.general.url = url
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Share...", style: .default, handler: { (_) in
|
||||
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
self.present(vc, animated: true)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
||||
present(alert, animated: true)
|
||||
router.present(router.moreOptions(forURL: url), animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -92,11 +92,11 @@ class ContentLabel: TTTAttributedLabel {
|
|||
}
|
||||
let text = (self.text as! NSString).substring(with: link.result.range)
|
||||
if let mention = getMention(for: url, text: text) {
|
||||
return navigationDelegate.viewController(forMention: mention)
|
||||
return navigationDelegate.router.profile(for: mention)
|
||||
} else if let tag = getHashtag(for: url, text: text) {
|
||||
return navigationDelegate.viewController(forTag: tag)
|
||||
return navigationDelegate.router.timeline(tag: tag)
|
||||
} else {
|
||||
return navigationDelegate.viewController(forURL: url)
|
||||
return navigationDelegate.router.safariViewController(for: url)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -193,11 +193,11 @@ class ActionNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
extension ActionNotificationTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
if avatarContainerView.frame.contains(location) {
|
||||
return delegate?.viewController(forAccount: notification.account.id)
|
||||
return delegate?.router.profile(for: notification.account.id)
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
return vc
|
||||
}
|
||||
return delegate?.viewController(forStatus: statusID)
|
||||
return delegate?.router.conversation(for: statusID)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import Pachyderm
|
|||
|
||||
class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||
|
||||
var router: AppRouter!
|
||||
|
||||
var delegate: StatusTableViewCellDelegate?
|
||||
|
||||
@IBOutlet weak var followLabel: UILabel!
|
||||
|
@ -99,6 +101,6 @@ class FollowNotificationTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
extension FollowNotificationTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
return delegate?.viewController(forAccount: accountID)
|
||||
return router.profile(for: accountID)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,13 +228,13 @@ extension ConversationMainStatusTableViewCell: AttachmentViewDelegate {
|
|||
extension ConversationMainStatusTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
if avatarImageView.frame.contains(location) {
|
||||
return delegate?.viewController(forAccount: accountID)
|
||||
return delegate?.router.profile(for: accountID)
|
||||
} else if attachmentsView.frame.contains(location) {
|
||||
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
|
||||
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
|
||||
let image = attachmentView.image!
|
||||
let description = attachmentView.description
|
||||
return delegate?.viewController(forImage: image, description: description, animatingFrom: attachmentView)
|
||||
return delegate?.largeImage(image, description: description, sourceView: attachmentView)
|
||||
}
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
|
|
|
@ -357,18 +357,18 @@ extension StatusTableViewCell: AttachmentViewDelegate {
|
|||
extension StatusTableViewCell: PreviewViewControllerProvider {
|
||||
func getPreviewViewController(forLocation location: CGPoint, sourceViewController: UIViewController) -> UIViewController? {
|
||||
if avatarImageView.frame.contains(location) {
|
||||
return delegate?.viewController(forAccount: accountID)
|
||||
return delegate?.router.profile(for: accountID)
|
||||
} else if attachmentsView.frame.contains(location) {
|
||||
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
|
||||
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
|
||||
let image = attachmentView.image!
|
||||
let description = attachmentView.attachment.description
|
||||
return delegate?.viewController(forImage: image, description: description, animatingFrom: attachmentView)
|
||||
return delegate?.largeImage(image, description: description, sourceView: attachmentView)
|
||||
}
|
||||
} else if contentLabel.frame.contains(location),
|
||||
let vc = contentLabel.getViewController(forLinkAt: contentLabel.convert(location, from: self)) {
|
||||
return vc
|
||||
}
|
||||
return delegate?.viewController(forStatus: statusID)
|
||||
return delegate?.router.conversation(for: statusID)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,15 +13,9 @@ import SwiftSoup
|
|||
struct XCBActions {
|
||||
|
||||
// MARK: - Utils
|
||||
private static func presentModally(_ vc: UIViewController, animated: Bool, completion: (() -> Void)? = nil) {
|
||||
UIApplication.shared.keyWindow!.rootViewController!.present(vc, animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
private static func presentNav(_ vc: UIViewController, animated: Bool) {
|
||||
let tabBarController = UIApplication.shared.keyWindow!.rootViewController! as! UITabBarController
|
||||
let navController = tabBarController.selectedViewController as! UINavigationController
|
||||
navController.pushViewController(vc, animated: animated)
|
||||
}
|
||||
private static var router: AppRouter = {
|
||||
return (UIApplication.shared.delegate as! AppDelegate).router
|
||||
}()
|
||||
|
||||
private static func getStatus(from request: XCBRequest, session: XCBSession, completion: @escaping (Status) -> Void) {
|
||||
if let id = request.arguments["statusID"] {
|
||||
|
@ -111,9 +105,9 @@ struct XCBActions {
|
|||
// MARK: - Statuses
|
||||
static func showStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
|
||||
getStatus(from: request, session: session) { (status) in
|
||||
let vc = ConversationTableViewController(for: status.id)
|
||||
let vc = router.conversation(for: status.id)
|
||||
DispatchQueue.main.async {
|
||||
presentNav(vc, animated: true)
|
||||
router.push(vc, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,10 +140,10 @@ struct XCBActions {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let compose = ComposeViewController(mentioningAcct: mentioning, text: text)
|
||||
let compose = router.compose(mentioningAcct: mentioning, text: text)
|
||||
compose.xcbSession = session
|
||||
let vc = UINavigationController(rootViewController: compose)
|
||||
presentModally(vc, animated: true)
|
||||
router.present(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,9 +207,9 @@ struct XCBActions {
|
|||
if silent ?? false {
|
||||
performAction(status: status, completion: nil)
|
||||
} else {
|
||||
let vc = ConversationTableViewController(for: status.id)
|
||||
let vc = router.conversation(for: status.id)
|
||||
DispatchQueue.main.async {
|
||||
presentNav(vc, animated: true)
|
||||
router.push(vc, animated: true)
|
||||
}
|
||||
let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
|
||||
|
@ -229,7 +223,7 @@ struct XCBActions {
|
|||
session.complete(with: .cancel)
|
||||
}))
|
||||
DispatchQueue.main.async {
|
||||
presentModally(alertController, animated: true)
|
||||
router.present(alertController, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,9 +234,9 @@ struct XCBActions {
|
|||
// MARK: - Accounts
|
||||
static func showAccount(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
|
||||
getAccount(from: request, session: session) { (account) in
|
||||
let vc = ProfileTableViewController(accountID: account.id)
|
||||
let vc = router.profile(for: account.id)
|
||||
DispatchQueue.main.async {
|
||||
presentNav(vc, animated: true)
|
||||
router.push(vc, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,9 +291,9 @@ struct XCBActions {
|
|||
if silent ?? false {
|
||||
performAction(account)
|
||||
} else {
|
||||
let vc = ProfileTableViewController(accountID: account.id)
|
||||
let vc = router.profile(for: account.id)
|
||||
DispatchQueue.main.async {
|
||||
presentNav(vc, animated: true)
|
||||
router.push(vc, animated: true)
|
||||
}
|
||||
let alertController = UIAlertController(title: "Follow \(account.realDisplayName)?", message: nil, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
|
||||
|
@ -309,7 +303,7 @@ struct XCBActions {
|
|||
session.complete(with: .cancel)
|
||||
}))
|
||||
DispatchQueue.main.async {
|
||||
presentModally(alertController, animated: true)
|
||||
router.present(alertController, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue