Replace global shared MastodonController instance with (mostly)

dependency injection

The places still using the .shared property are cases where there is no
view controller from which to (easily) get the appropriate instance,
such as user activity and X-Callback-URL handling. These uses will need
to be revisited once there are multiple MastodonControllers.

See #16
This commit is contained in:
Shadowfacts 2020-01-05 15:25:07 -05:00
parent a18bcac8b8
commit 2bdcb9b7f8
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
44 changed files with 286 additions and 157 deletions

View File

@ -118,6 +118,7 @@
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */; };
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */; };
D64BC19023C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */; };
D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC19123C271D9000D0238 /* MastodonActivity.swift */; };
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64F80E1215875CC00BEF393 /* XCBActionType.swift */; };
@ -388,6 +389,7 @@
D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnpinStatusActivity.swift; sourceTree = "<group>"; };
D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowRequestNotificationTableViewCell.swift; sourceTree = "<group>"; };
D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowRequestNotificationTableViewCell.xib; sourceTree = "<group>"; };
D64BC19123C271D9000D0238 /* MastodonActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonActivity.swift; sourceTree = "<group>"; };
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
D64F80E1215875CC00BEF393 /* XCBActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActionType.swift; sourceTree = "<group>"; };
@ -1056,6 +1058,7 @@
children = (
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */,
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */,
D64BC19123C271D9000D0238 /* MastodonActivity.swift */,
D6AEBB4623216B0C00E5038B /* Account Activities */,
D627943323A5523800D38C68 /* Status Activities */,
);
@ -1670,6 +1673,7 @@
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */,
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */,
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,

View File

@ -9,7 +9,7 @@
import UIKit
import Pachyderm
class AccountActivity: UIActivity {
class AccountActivity: MastodonActivity {
override class var activityCategory: UIActivity.Category {
return .action

View File

@ -28,7 +28,7 @@ class FollowAccountActivity: AccountActivity {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
let request = Account.follow(account.id)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(relationship, _) = response {
MastodonCache.add(relationship: relationship)
} else {

View File

@ -28,7 +28,7 @@ class SendMessageActivity: AccountActivity {
override var activityViewController: UIViewController? {
guard let account = account else { return nil }
return UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct))
return UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct, mastodonController: mastodonController))
}
}

View File

@ -28,7 +28,7 @@ class UnfollowAccountActivity: AccountActivity {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
let request = Account.unfollow(account.id)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(relationship, _) = response {
MastodonCache.add(relationship: relationship)
} else {

View File

@ -0,0 +1,15 @@
//
// MastodonActivity.swift
// Tusker
//
// Created by Shadowfacts on 1/5/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
class MastodonActivity: UIActivity {
var mastodonController: MastodonController {
MastodonController.shared
}
}

View File

@ -27,7 +27,7 @@ class BookmarkStatusActivity: StatusActivity {
guard let status = status else { return }
let request = Status.bookmark(status)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(status, _) = response {
MastodonCache.add(status: status)
} else {

View File

@ -26,7 +26,7 @@ class PinStatusActivity: StatusActivity {
guard let status = status else { return }
let request = Status.pin(status)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(status, _) = response {
MastodonCache.add(status: status)
} else {

View File

@ -9,7 +9,7 @@
import UIKit
import Pachyderm
class StatusActivity: UIActivity {
class StatusActivity: MastodonActivity {
override class var activityCategory: UIActivity.Category {
return .action

View File

@ -27,7 +27,7 @@ class UnbookmarkStatusActivity: StatusActivity {
guard let status = status else { return }
let request = Status.unbookmark(status)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(status, _) = response {
MastodonCache.add(status: status)
} else {

View File

@ -26,7 +26,7 @@ class UnpinStatusActivity: StatusActivity {
guard let status = status else { return }
let request = Status.unpin(status)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(status, _) = response {
MastodonCache.add(status: status)
} else {

View File

@ -13,6 +13,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let mastodonController = MastodonController.shared
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppShortcutItem.createItems(for: application)
@ -95,11 +97,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func showAppUI() {
MastodonController.createClient()
MastodonController.getOwnAccount()
MastodonController.getOwnInstance()
mastodonController.createClient()
mastodonController.getOwnAccount()
mastodonController.getOwnInstance()
let tabBarController = MainTabBarViewController()
let tabBarController = MainTabBarViewController(mastodonController: mastodonController)
window!.rootViewController = tabBarController
}

View File

@ -11,32 +11,33 @@ import Pachyderm
class MastodonController {
private static var client: Client!
@available(*, deprecated, message: "Use dependency injection to obtain an instance")
static let shared = MastodonController()
static var account: Account!
static var instance: Instance!
private var client: Client!
static var accessToken: String? {
var account: Account!
var instance: Instance!
var accessToken: String? {
client?.accessToken
}
private init() {}
func createClient(instanceURL: URL = LocalData.shared.instanceURL!) {
client = Client(baseURL: instanceURL)
static func createClient() {
guard let url = LocalData.shared.instanceURL else { fatalError("Can't connect without instance URL") }
client = Client(baseURL: url)
client.clientID = LocalData.shared.clientID
client.clientSecret = LocalData.shared.clientSecret
client.accessToken = LocalData.shared.accessToken
if instanceURL == LocalData.shared.instanceURL {
client.clientID = LocalData.shared.clientID
client.clientSecret = LocalData.shared.clientSecret
client.accessToken = LocalData.shared.accessToken
}
}
static func run<Result>(_ request: Request<Result>, completion: @escaping Client.Callback<Result>) {
func run<Result>(_ request: Request<Result>, completion: @escaping Client.Callback<Result>) {
client.run(request, completion: completion)
}
static func registerApp(completion: @escaping () -> Void) {
func registerApp(completion: @escaping () -> Void) {
guard LocalData.shared.clientID == nil,
LocalData.shared.clientSecret == nil else {
completion()
@ -51,7 +52,7 @@ class MastodonController {
}
}
static func authorize(authorizationCode: String, completion: @escaping () -> Void) {
func authorize(authorizationCode: String, completion: @escaping () -> Void) {
client.getAccessToken(authorizationCode: authorizationCode, redirectURI: "tusker://oauth") { response in
guard case let .success(settings, _) = response else { fatalError() }
LocalData.shared.accessToken = settings.accessToken
@ -59,7 +60,7 @@ class MastodonController {
}
}
static func getOwnAccount(completion: ((Account) -> Void)? = nil) {
func getOwnAccount(completion: ((Account) -> Void)? = nil) {
if account != nil {
completion?(account)
} else {
@ -73,7 +74,7 @@ class MastodonController {
}
}
static func getOwnInstance() {
func getOwnInstance() {
let request = Client.getInstance()
run(request) { (response) in
guard case let .success(instance, _) = response else { fatalError() }

View File

@ -20,6 +20,8 @@ class MastodonCache {
static let statusSubject = PassthroughSubject<Status, Never>()
static let accountSubject = PassthroughSubject<Account, Never>()
static var mastodonController: MastodonController { .shared }
// MARK: - Statuses
static func status(for id: String) -> Status? {
return statuses[id]
@ -38,7 +40,7 @@ class MastodonCache {
static func status(for id: String, completion: @escaping (Status?) -> Void) {
let request = Client.getStatus(id: id)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(status, _) = response else {
completion(nil)
return
@ -68,7 +70,7 @@ class MastodonCache {
static func account(for id: String, completion: @escaping (Account?) -> Void) {
let request = Client.getAccount(id: id)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(account, _) = response else {
completion(nil)
return
@ -97,7 +99,7 @@ class MastodonCache {
static func relationship(for id: String, completion: @escaping (Relationship?) -> Void) {
let request = Client.getRelationships(accounts: [id])
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(relationships, _) = response,
let relationship = relationships.first else {
completion(nil)

View File

@ -12,10 +12,13 @@ class AccountListTableViewController: EnhancedTableViewController {
private let accountCell = "accountCell"
let mastodonController: MastodonController
let accountIDs: [String]
init(accountIDs: [String]) {
init(accountIDs: [String], mastodonController: MastodonController) {
self.accountIDs = accountIDs
self.mastodonController = mastodonController
super.init(style: .grouped)
}
@ -58,4 +61,6 @@ class AccountListTableViewController: EnhancedTableViewController {
}
extension AccountListTableViewController: TuskerNavigationDelegate {}
extension AccountListTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}

View File

@ -13,6 +13,8 @@ class BookmarksTableViewController: EnhancedTableViewController {
private let statusCell = "statusCell"
let mastodonController: MastodonController
var statuses: [(id: String, state: StatusState)] = [] {
didSet {
DispatchQueue.main.async {
@ -24,7 +26,9 @@ class BookmarksTableViewController: EnhancedTableViewController {
var newer: RequestRange?
var older: RequestRange?
init() {
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
super.init(style: .plain)
title = NSLocalizedString("Bookmarks", comment: "bookmarks screen title")
@ -45,7 +49,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
tableView.prefetchDataSource = self
let request = Client.getBookmarks()
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(statuses, pagination) = response else { fatalError() }
MastodonCache.addAll(statuses: statuses)
self.statuses.append(contentsOf: statuses.map { ($0.id, .unknown) })
@ -82,7 +86,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
}
let request = Client.getBookmarks(range: older)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(newStatuses, pagination) = response else { fatalError() }
self.older = pagination?.older
MastodonCache.addAll(statuses: newStatuses)
@ -107,7 +111,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
let unbookmarkAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Unbookmark", comment: "unbookmark action title")) { (action, view, completion) in
let request = Status.unbookmark(status)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case let .success(newStatus, _) = response else { fatalError() }
MastodonCache.add(status: newStatus)
self.statuses.remove(at: indexPath.row)
@ -131,7 +135,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
return [
UIAction(title: NSLocalizedString("Unbookmark", comment: "unbookmark action title"), image: UIImage(systemName: "bookmark.fill"), identifier: .init("unbookmark"), discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
let request = Status.unbookmark(status)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case let .success(newStatus, _) = response else { fatalError() }
MastodonCache.add(status: newStatus)
self.statuses.remove(at: indexPath.row)
@ -143,6 +147,8 @@ class BookmarksTableViewController: EnhancedTableViewController {
}
extension BookmarksTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates()
tableView.endUpdates()

View File

@ -12,6 +12,8 @@ import Intents
class ComposeViewController: UIViewController {
let mastodonController: MastodonController
var inReplyToID: String?
var accountsToMention: [String]
var initialText: String?
@ -64,7 +66,9 @@ class ComposeViewController: UIViewController {
@IBOutlet weak var postProgressView: SteppedProgressView!
init(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil) {
init(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil, mastodonController: MastodonController) {
self.mastodonController = mastodonController
self.inReplyToID = inReplyToID
if let inReplyToID = inReplyToID, let inReplyTo = MastodonCache.status(for: inReplyToID) {
accountsToMention = [inReplyTo.account.acct] + inReplyTo.mentions.map { $0.acct }
@ -73,7 +77,7 @@ class ComposeViewController: UIViewController {
} else {
accountsToMention = []
}
if let ownAccount = MastodonController.account {
if let ownAccount = mastodonController.account {
accountsToMention.removeAll(where: { acct in ownAccount.acct == acct })
}
accountsToMention = accountsToMention.uniques()
@ -120,7 +124,7 @@ class ComposeViewController: UIViewController {
statusTextView.text = accountsToMention.map({ acct in "@\(acct) " }).joined()
initialText = statusTextView.text
MastodonController.getOwnAccount { (account) in
mastodonController.getOwnAccount { (account) in
DispatchQueue.main.async {
self.selfDetailView.update(account: account)
}
@ -270,7 +274,7 @@ class ComposeViewController: UIViewController {
// TODO: include CW char count
let count = CharacterCounter.count(text: statusTextView.text)
let cwCount = contentWarningEnabled ? (contentWarningTextField.text?.count ?? 0) : 0
let remaining = (MastodonController.instance.maxStatusCharacters ?? 500) - count - cwCount
let remaining = (mastodonController.instance.maxStatusCharacters ?? 500) - count - cwCount
if remaining < 0 {
charactersRemainingLabel.textColor = .red
postBarButtonItem.isEnabled = false
@ -296,7 +300,7 @@ class ComposeViewController: UIViewController {
}
func updateAddAttachmentButton() {
switch MastodonController.instance.instanceType {
switch mastodonController.instance.instanceType {
case .pleroma:
addAttachmentButton.isEnabled = true
case .mastodon:
@ -481,7 +485,7 @@ class ComposeViewController: UIViewController {
self.postProgressView.step()
let request = Client.upload(attachment: FormAttachment(mimeType: mimeType, data: data, fileName: "file"), description: description)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case let .success(attachment, _) = response else { fatalError() }
attachments[index] = attachment
@ -507,7 +511,7 @@ class ComposeViewController: UIViewController {
spoilerText: contentWarning,
visibility: visibility,
language: nil)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case let .success(status, _) = response else { fatalError() }
self.postedStatus = status
MastodonCache.add(status: status)
@ -520,7 +524,7 @@ class ComposeViewController: UIViewController {
self.postProgressView.step()
self.dismiss(animated: true)
let conversationVC = ConversationTableViewController(for: status.id)
let conversationVC = ConversationTableViewController(for: status.id, mastodonController: self.mastodonController)
self.show(conversationVC, sender: self)
self.xcbSession?.complete(with: .success, additionalData: [
@ -561,7 +565,7 @@ extension ComposeViewController: UITextViewDelegate {
extension ComposeViewController: AssetPickerViewControllerDelegate {
func assetPicker(_ assetPicker: AssetPickerViewController, shouldAllowAssetOfType type: CompositionAttachment.AttachmentType) -> Bool {
switch MastodonController.instance.instanceType {
switch mastodonController.instance.instanceType {
case .pleroma:
return true
case .mastodon:

View File

@ -15,6 +15,8 @@ class ConversationTableViewController: EnhancedTableViewController {
static let showPostsImage = UIImage(systemName: "eye.fill")!
static let hidePostsImage = UIImage(systemName: "eye.slash.fill")!
let mastodonController: MastodonController
let mainStatusID: String
let mainStatusState: StatusState
var statuses: [(id: String, state: StatusState)] = [] {
@ -28,9 +30,10 @@ class ConversationTableViewController: EnhancedTableViewController {
var showStatusesAutomatically = false
var visibilityBarButtonItem: UIBarButtonItem!
init(for mainStatusID: String, state: StatusState = .unknown) {
init(for mainStatusID: String, state: StatusState = .unknown, mastodonController: MastodonController) {
self.mainStatusID = mainStatusID
self.mainStatusState = state
self.mastodonController = mastodonController
super.init(style: .plain)
}
@ -58,7 +61,7 @@ class ConversationTableViewController: EnhancedTableViewController {
guard let mainStatus = MastodonCache.status(for: mainStatusID) else { fatalError("Missing cached status \(mainStatusID)") }
let request = Status.getContext(mainStatus)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(context, _) = response else { fatalError() }
let parents = self.getDirectParents(of: mainStatus, from: context.ancestors)
MastodonCache.addAll(statuses: parents)
@ -155,6 +158,7 @@ class ConversationTableViewController: EnhancedTableViewController {
}
extension ConversationTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()

View File

@ -12,12 +12,16 @@ import Pachyderm
class ExploreViewController: EnhancedTableViewController {
let mastodonController: MastodonController
var dataSource: DataSource!
var resultsController: SearchResultsViewController!
var searchController: UISearchController!
init() {
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
super.init(style: .insetGrouped)
title = NSLocalizedString("Explore", comment: "explore tab title")
@ -88,7 +92,7 @@ class ExploreViewController: EnhancedTableViewController {
dataSource.apply(snapshot)
}
resultsController = SearchResultsViewController()
resultsController = SearchResultsViewController(mastodonController: mastodonController)
resultsController.exploreNavigationController = self.navigationController!
searchController = UISearchController(searchResultsController: resultsController)
searchController.searchResultsUpdater = resultsController
@ -107,7 +111,7 @@ class ExploreViewController: EnhancedTableViewController {
func reloadLists() {
let request = Client.getLists()
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(lists, _) = response else {
fatalError()
}
@ -143,7 +147,7 @@ class ExploreViewController: EnhancedTableViewController {
alert.addAction(UIAlertAction(title: NSLocalizedString("Delete List", comment: "delete list alert confirm button"), style: .destructive, handler: { (_) in
let request = List.delete(list)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case .success(_, _) = response else {
fatalError()
}
@ -174,10 +178,10 @@ class ExploreViewController: EnhancedTableViewController {
return
case .bookmarks:
show(BookmarksTableViewController(), sender: nil)
show(BookmarksTableViewController(mastodonController: mastodonController), sender: nil)
case let .list(list):
show(ListTimelineViewController(for: list), sender: nil)
show(ListTimelineViewController(for: list, mastodonController: mastodonController), sender: nil)
case .addList:
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
@ -190,13 +194,13 @@ class ExploreViewController: EnhancedTableViewController {
}
let request = Client.createList(title: title)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case let .success(list, _) = response else { fatalError() }
self.reloadLists()
DispatchQueue.main.async {
let listTimelineController = ListTimelineViewController(for: list)
let listTimelineController = ListTimelineViewController(for: list, mastodonController: self.mastodonController)
listTimelineController.presentEditOnAppear = true
self.show(listTimelineController, sender: nil)
}
@ -205,11 +209,11 @@ class ExploreViewController: EnhancedTableViewController {
present(alert, animated: true)
case let .savedHashtag(hashtag):
show(HashtagTimelineViewController(for: hashtag), sender: nil)
show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
case .addSavedHashtag:
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
let navController = UINavigationController(rootViewController: AddSavedHashtagViewController())
let navController = UINavigationController(rootViewController: AddSavedHashtagViewController(mastodonController: mastodonController))
present(navController, animated: true)
case let .savedInstance(url):

View File

@ -11,6 +11,8 @@ import Pachyderm
class EditListAccountsViewController: EnhancedTableViewController {
let mastodonController: MastodonController
let list: List
var dataSource: DataSource!
@ -20,8 +22,9 @@ class EditListAccountsViewController: EnhancedTableViewController {
var searchResultsController: SearchResultsViewController!
var searchController: UISearchController!
init(list: List) {
init(list: List, mastodonController: MastodonController) {
self.list = list
self.mastodonController = mastodonController
super.init(style: .plain)
@ -49,7 +52,7 @@ class EditListAccountsViewController: EnhancedTableViewController {
})
dataSource.editListAccountsController = self
searchResultsController = SearchResultsViewController()
searchResultsController = SearchResultsViewController(mastodonController: mastodonController)
searchResultsController.delegate = self
searchResultsController.onlySections = [.accounts]
searchController = UISearchController(searchResultsController: searchResultsController)
@ -70,7 +73,7 @@ class EditListAccountsViewController: EnhancedTableViewController {
func loadAccounts() {
let request = List.getAccounts(list)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(accounts, pagination) = response else {
fatalError()
}
@ -109,7 +112,7 @@ class EditListAccountsViewController: EnhancedTableViewController {
fatalError()
}
let request = List.update(self.list, title: text)
MastodonController.run(request) { (response) in
self.mastodonController.run(request) { (response) in
guard case .success(_, _) = response else {
fatalError()
}
@ -143,7 +146,7 @@ extension EditListAccountsViewController {
}
let request = List.remove(editListAccountsController!.list, accounts: [id])
MastodonController.run(request) { (response) in
editListAccountsController!.mastodonController.run(request) { (response) in
guard case .success(_, _) = response else {
fatalError()
}
@ -157,7 +160,7 @@ extension EditListAccountsViewController {
extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
func selectedSearchResult(account accountID: String) {
let request = List.add(list, accounts: [accountID])
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case .success(_, _) = response else {
fatalError()
}

View File

@ -15,10 +15,10 @@ class ListTimelineViewController: TimelineTableViewController {
var presentEditOnAppear = false
init(for list: List) {
init(for list: List, mastodonController: MastodonController) {
self.list = list
super.init(for: .list(id: list.id))
super.init(for: .list(id: list.id), mastodonController: mastodonController)
title = list.title
}
@ -42,7 +42,7 @@ class ListTimelineViewController: TimelineTableViewController {
}
func presentEdit(animated: Bool) {
let editListAccountsController = EditListAccountsViewController(list: list)
let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController)
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonPressed))
let navController = UINavigationController(rootViewController: editListAccountsController)
present(navController, animated: animated)

View File

@ -10,17 +10,29 @@ import UIKit
class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
let mastodonController: MastodonController
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
viewControllers = [
embedInNavigationController(TimelinesPageViewController()),
embedInNavigationController(NotificationsPageViewController()),
ComposeViewController(),
embedInNavigationController(ExploreViewController()),
embedInNavigationController(MyProfileTableViewController()),
embedInNavigationController(TimelinesPageViewController(mastodonController: mastodonController)),
embedInNavigationController(NotificationsPageViewController(mastodonController: mastodonController)),
ComposeViewController(mastodonController: mastodonController),
embedInNavigationController(ExploreViewController(mastodonController: mastodonController)),
embedInNavigationController(MyProfileTableViewController(mastodonController: mastodonController)),
]
}
@ -41,7 +53,7 @@ class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
}
func presentCompose() {
let compose = ComposeViewController()
let compose = ComposeViewController(mastodonController: mastodonController)
let navigationController = embedInNavigationController(compose)
navigationController.presentationController?.delegate = compose
present(navigationController, animated: true)

View File

@ -14,12 +14,16 @@ class NotificationsPageViewController: SegmentedPageViewController {
private let notificationsTitle = NSLocalizedString("Notifications", comment: "notifications tab title")
private let mentionsTitle = NSLocalizedString("Mentions", comment: "mentions tab title")
init() {
let notifications = NotificationsTableViewController(allowedTypes: Pachyderm.Notification.Kind.allCases)
let mastodonController: MastodonController
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
let notifications = NotificationsTableViewController(allowedTypes: Pachyderm.Notification.Kind.allCases, mastodonController: mastodonController)
notifications.title = notificationsTitle
notifications.userActivity = UserActivityManager.checkNotificationsActivity()
let mentions = NotificationsTableViewController(allowedTypes: [.mention])
let mentions = NotificationsTableViewController(allowedTypes: [.mention], mastodonController: mastodonController)
mentions.title = mentionsTitle
mentions.userActivity = UserActivityManager.checkMentionsActivity()

View File

@ -16,6 +16,8 @@ class NotificationsTableViewController: EnhancedTableViewController {
private let followGroupCell = "followGroupCell"
private let followRequestCell = "followRequestCell"
let mastodonController: MastodonController
let excludedTypes: [Pachyderm.Notification.Kind]
let groupTypes = [Notification.Kind.favourite, .reblog, .follow]
@ -30,8 +32,9 @@ class NotificationsTableViewController: EnhancedTableViewController {
var newer: RequestRange?
var older: RequestRange?
init(allowedTypes: [Pachyderm.Notification.Kind]) {
init(allowedTypes: [Pachyderm.Notification.Kind], mastodonController: MastodonController) {
self.excludedTypes = Array(Set(Pachyderm.Notification.Kind.allCases).subtracting(allowedTypes))
self.mastodonController = mastodonController
super.init(style: .plain)
@ -57,7 +60,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
tableView.prefetchDataSource = self
let request = Client.getNotifications(excludeTypes: excludedTypes)
MastodonController.run(request) { result in
mastodonController.run(request) { result in
guard case let .success(notifications, pagination) = result else { fatalError() }
let groups = NotificationGroup.createGroups(notifications: notifications, only: self.groupTypes)
@ -125,7 +128,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
guard let older = older else { return }
let request = Client.getNotifications(excludeTypes: excludedTypes, range: older)
MastodonController.run(request) { result in
mastodonController.run(request) { result in
guard case let .success(newNotifications, pagination) = result else { fatalError() }
let groups = NotificationGroup.createGroups(notifications: newNotifications, only: self.groupTypes)
@ -182,7 +185,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
.map(Pachyderm.Notification.dismiss(id:))
.forEach { (request) in
group.enter()
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
group.leave()
}
}
@ -197,7 +200,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
guard let newer = newer else { return }
let request = Client.getNotifications(excludeTypes: excludedTypes, range: newer)
MastodonController.run(request) { result in
mastodonController.run(request) { result in
guard case let .success(newNotifications, pagination) = result else { fatalError() }
let groups = NotificationGroup.createGroups(notifications: newNotifications, only: self.groupTypes)
@ -222,6 +225,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
}
extension NotificationsTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()

View File

@ -46,8 +46,9 @@ class OnboardingViewController: UINavigationController {
extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate {
func didSelectInstance(url: URL) {
LocalData.shared.instanceURL = url
MastodonController.createClient()
MastodonController.registerApp {
let mastodonController = MastodonController.shared
mastodonController.createClient()
mastodonController.registerApp {
let clientID = LocalData.shared.clientID!
let callbackURL = "tusker://oauth"
@ -69,7 +70,7 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
let item = components.queryItems?.first(where: { $0.name == "code" }),
let authCode = item.value else { return }
MastodonController.authorize(authorizationCode: authCode) {
mastodonController.authorize(authorizationCode: authCode) {
DispatchQueue.main.async {
self.onboardingDelegate?.didFinishOnboarding()
}

View File

@ -11,14 +11,14 @@ import SwiftUI
class MyProfileTableViewController: ProfileTableViewController {
init() {
super.init(accountID: nil)
init(mastodonController: MastodonController) {
super.init(accountID: nil, mastodonController: mastodonController)
title = "My Profile"
tabBarItem.image = UIImage(systemName: "person.fill")
MastodonController.getOwnAccount { (account) in
mastodonController.getOwnAccount { (account) in
self.accountID = account.id
ImageCache.avatars.get(account.avatar, completion: { (data) in

View File

@ -12,6 +12,8 @@ import SafariServices
class ProfileTableViewController: EnhancedTableViewController {
let mastodonController: MastodonController
var accountID: String! {
didSet {
if shouldLoadOnAccountIDSet {
@ -43,7 +45,9 @@ class ProfileTableViewController: EnhancedTableViewController {
var shouldLoadOnAccountIDSet = false
var loadingVC: LoadingViewController? = nil
init(accountID: String?) {
init(accountID: String?, mastodonController: MastodonController) {
self.mastodonController = mastodonController
self.accountID = accountID
super.init(style: .plain)
@ -130,12 +134,12 @@ class ProfileTableViewController: EnhancedTableViewController {
func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) {
let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: onlyPinned, excludeReplies: !Preferences.shared.showRepliesInProfiles)
MastodonController.run(request, completion: completion)
mastodonController.run(request, completion: completion)
}
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, mastodonController: mastodonController))
present(vc, animated: true)
}
@ -233,6 +237,8 @@ class ProfileTableViewController: EnhancedTableViewController {
}
extension ProfileTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()

View File

@ -28,6 +28,8 @@ extension SearchResultsViewControllerDelegate {
class SearchResultsViewController: EnhancedTableViewController {
let mastodonController: MastodonController!
weak var exploreNavigationController: UINavigationController?
weak var delegate: SearchResultsViewControllerDelegate?
@ -40,7 +42,9 @@ class SearchResultsViewController: EnhancedTableViewController {
let searchSubject = PassthroughSubject<String?, Never>()
var currentQuery: String?
init() {
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
super.init(style: .grouped)
title = NSLocalizedString("Search", comment: "search screen title")
@ -118,7 +122,7 @@ class SearchResultsViewController: EnhancedTableViewController {
}
let request = Client.search(query: query, resolve: true, limit: 10)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(results, _) = response else { fatalError() }
DispatchQueue.main.async {
@ -217,6 +221,7 @@ extension SearchResultsViewController: UISearchBarDelegate {
}
extension SearchResultsViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates()
tableView.endUpdates()

View File

@ -14,6 +14,8 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
private let statusCell = "statusCell"
private let accountCell = "accountCell"
let mastodonController: MastodonController
let actionType: ActionType
let statusID: String
var statusState: StatusState
@ -32,8 +34,11 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
- Parameter actionType The action that this VC is for.
- Parameter statusID The ID of the status to show.
- Parameter accountIDs The accounts that will be shown. If `nil` is passed, a request will be performed to load the accounts.
- Parameter mastodonController The `MastodonController` instance this view controller uses.
*/
init(actionType: ActionType, statusID: String, statusState: StatusState, accountIDs: [String]?) {
init(actionType: ActionType, statusID: String, statusState: StatusState, accountIDs: [String]?, mastodonController: MastodonController) {
self.mastodonController = mastodonController
self.actionType = actionType
self.statusID = statusID
self.statusState = statusState
@ -75,7 +80,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
tableView.tableFooterView = UIActivityIndicatorView(style: .large)
let request = actionType == .favorite ? Status.getFavourites(status) : Status.getReblogs(status)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
guard case let .success(accounts, _) = response else { fatalError() }
MastodonCache.addAll(accounts: accounts)
DispatchQueue.main.async {
@ -137,6 +142,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
}
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()

View File

@ -22,10 +22,10 @@ class HashtagTimelineViewController: TimelineTableViewController {
}
}
init(for hashtag: Hashtag) {
init(for hashtag: Hashtag, mastodonController: MastodonController) {
self.hashtag = hashtag
super.init(for: .tag(hashtag: hashtag.name))
super.init(for: .tag(hashtag: hashtag.name), mastodonController: mastodonController)
}
required init?(coder aDecoder: NSCoder) {

View File

@ -31,7 +31,10 @@ class InstanceTimelineViewController: TimelineTableViewController {
init(for url: URL) {
self.instanceURL = url
super.init(for: .instance(instanceURL: url))
let mastodonController = MastodonController()
mastodonController.createClient(instanceURL: url)
super.init(for: .instance(instanceURL: url), mastodonController: mastodonController)
}
required init?(coder aDecoder: NSCoder) {

View File

@ -12,6 +12,7 @@ import Pachyderm
class TimelineTableViewController: EnhancedTableViewController {
var timeline: Timeline!
let mastodonController: MastodonController
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
didSet {
@ -24,8 +25,9 @@ class TimelineTableViewController: EnhancedTableViewController {
var newer: RequestRange?
var older: RequestRange?
init(for timeline: Timeline) {
init(for timeline: Timeline, mastodonController: MastodonController) {
self.timeline = timeline
self.mastodonController = mastodonController
super.init(style: .plain)
@ -56,13 +58,13 @@ class TimelineTableViewController: EnhancedTableViewController {
tableView.prefetchDataSource = self
guard MastodonController.accessToken != nil else { return }
guard mastodonController.accessToken != nil else { return }
loadInitialStatuses()
}
func loadInitialStatuses() {
let request = Client.getStatuses(timeline: timeline)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(statuses, pagination) = response else { fatalError() }
MastodonCache.addAll(statuses: statuses)
self.timelineSegments.insert(statuses.map { ($0.id, .unknown) }, at: 0)
@ -100,7 +102,7 @@ class TimelineTableViewController: EnhancedTableViewController {
guard let older = older else { return }
let request = Client.getStatuses(timeline: timeline, range: older)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(newStatuses, pagination) = response else { fatalError() }
self.older = pagination?.older
MastodonCache.addAll(statuses: newStatuses)
@ -125,7 +127,7 @@ class TimelineTableViewController: EnhancedTableViewController {
guard let newer = newer else { return }
let request = Client.getStatuses(timeline: timeline, range: newer)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
guard case let .success(newStatuses, pagination) = response else { fatalError() }
self.newer = pagination?.newer
MastodonCache.addAll(statuses: newStatuses)
@ -146,6 +148,8 @@ class TimelineTableViewController: EnhancedTableViewController {
}
extension TimelineTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()

View File

@ -14,14 +14,18 @@ class TimelinesPageViewController: SegmentedPageViewController {
private let federatedTitle = NSLocalizedString("Federated", comment: "federated timeline tab title")
private let localTitle = NSLocalizedString("Local", comment: "local timeline tab title")
init() {
let home = TimelineTableViewController(for: .home)
let mastodonController: MastodonController
init(mastodonController: MastodonController) {
self.mastodonController = mastodonController
let home = TimelineTableViewController(for: .home, mastodonController: mastodonController)
home.title = homeTitle
let federated = TimelineTableViewController(for: .public(local: false))
let federated = TimelineTableViewController(for: .public(local: false), mastodonController: mastodonController)
federated.title = federatedTitle
let local = TimelineTableViewController(for: .public(local: true))
let local = TimelineTableViewController(for: .public(local: true), mastodonController: mastodonController)
local.title = localTitle
super.init(titles: [

View File

@ -15,6 +15,8 @@ class UserActivityManager {
private static let encoder = PropertyListEncoder()
private static let decoder = PropertyListDecoder()
private static var mastodonController: MastodonController { .shared }
private static func getMainTabBarController() -> MainTabBarViewController {
return (UIApplication.shared.delegate! as! AppDelegate).window!.rootViewController as! MainTabBarViewController
}
@ -42,7 +44,8 @@ class UserActivityManager {
static func handleNewPost(activity: NSUserActivity) {
// TODO: check not currently showing compose screen
let mentioning = activity.userInfo?["mentioning"] as? String
present(UINavigationController(rootViewController: ComposeViewController(mentioningAcct: mentioning)))
let composeVC = ComposeViewController(mentioningAcct: mentioning, mastodonController: mastodonController)
present(UINavigationController(rootViewController: composeVC))
}
// MARK: - Check Notifications
@ -144,7 +147,8 @@ class UserActivityManager {
rootController.segmentedControl.selectedSegmentIndex = index
rootController.selectPage(at: index, animated: false)
default:
navigationController.pushViewController(TimelineTableViewController(for: timeline), animated: false)
let timeline = TimelineTableViewController(for: timeline, mastodonController: mastodonController)
navigationController.pushViewController(timeline, animated: false)
}
}
@ -182,7 +186,7 @@ class UserActivityManager {
tabBarController.select(tab: .explore)
if let navigationController = tabBarController.getTabController(tab: .explore) as? UINavigationController {
navigationController.popToRootViewController(animated: false)
navigationController.pushViewController(BookmarksTableViewController(), animated: false)
navigationController.pushViewController(BookmarksTableViewController(mastodonController: mastodonController), animated: false)
}
}

View File

@ -12,6 +12,8 @@ import Pachyderm
protocol TuskerNavigationDelegate {
var apiController: MastodonController { get }
func show(_ vc: UIViewController)
func selected(account accountID: String)
@ -68,15 +70,15 @@ extension TuskerNavigationDelegate where Self: UIViewController {
return
}
show(ProfileTableViewController(accountID: accountID), sender: self)
show(ProfileTableViewController(accountID: accountID, mastodonController: apiController), sender: self)
}
func selected(mention: Mention) {
show(ProfileTableViewController(accountID: mention.id), sender: self)
show(ProfileTableViewController(accountID: mention.id, mastodonController: apiController), sender: self)
}
func selected(tag: Hashtag) {
show(HashtagTimelineViewController(for: tag), sender: self)
show(HashtagTimelineViewController(for: tag, mastodonController: apiController), sender: self)
}
func selected(url: URL) {
@ -113,7 +115,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
return
}
show(ConversationTableViewController(for: statusID, state: state), sender: self)
show(ConversationTableViewController(for: statusID, state: state, mastodonController: apiController), sender: self)
}
// protocols can't have parameter defaults, so this stub is necessary to fulfill the protocol req
@ -122,14 +124,14 @@ extension TuskerNavigationDelegate where Self: UIViewController {
}
func compose(mentioning: String? = nil) {
let compose = ComposeViewController( mentioningAcct: mentioning)
let compose = ComposeViewController(mentioningAcct: mentioning, mastodonController: apiController)
let vc = UINavigationController(rootViewController: compose)
vc.presentationController?.delegate = compose
present(vc, animated: true)
}
func reply(to statusID: String) {
let compose = ComposeViewController(inReplyTo: statusID)
let compose = ComposeViewController(inReplyTo: statusID, mastodonController: apiController)
let vc = UINavigationController(rootViewController: compose)
vc.presentationController?.delegate = compose
present(vc, animated: true)
@ -200,7 +202,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
customActivites.insert(bookmarked ? UnbookmarkStatusActivity() : BookmarkStatusActivity(), at: 0)
}
if status.account == MastodonController.account,
if status.account == apiController.account,
let pinned = status.pinned {
customActivites.insert(pinned ? UnpinStatusActivity() : PinStatusActivity(), at: 1)
}
@ -228,13 +230,13 @@ extension TuskerNavigationDelegate where Self: UIViewController {
}
func showFollowedByList(accountIDs: [String]) {
let vc = AccountListTableViewController(accountIDs: accountIDs)
let vc = AccountListTableViewController(accountIDs: accountIDs, mastodonController: apiController)
vc.title = NSLocalizedString("Followed By", comment: "followed by accounts list title")
show(vc, sender: self)
}
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, statusState state: StatusState, accountIDs: [String]?) -> StatusActionAccountListTableViewController {
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, statusState: state, accountIDs: accountIDs)
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, statusState: state, accountIDs: accountIDs, mastodonController: apiController)
}
}

View File

@ -11,6 +11,7 @@ import UIKit
class AccountTableViewCell: UITableViewCell {
var delegate: TuskerNavigationDelegate?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel!
@ -68,6 +69,11 @@ extension AccountTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) })
guard let mastodonController = mastodonController else { return nil }
return (content: {
ProfileTableViewController(accountID: self.accountID, mastodonController: mastodonController)
}, actions: {
self.actionsForProfile(accountID: self.accountID)
})
}
}

View File

@ -178,13 +178,15 @@ class ContentLabel: LinkLabel {
func getViewController(forLink url: URL, inRange range: NSRange) -> UIViewController {
let text = (self.text! as NSString).substring(with: range)
if let mention = getMention(for: url, text: text) {
return ProfileTableViewController(accountID: mention.id)
} else if let tag = getHashtag(for: url, text: text) {
return HashtagTimelineViewController(for: tag)
} else {
return SFSafariViewController(url: url)
if let navigationDelegate = navigationDelegate {
if let mention = getMention(for: url, text: text) {
return ProfileTableViewController(accountID: mention.id, mastodonController: navigationDelegate.apiController)
} else if let tag = getHashtag(for: url, text: text) {
return HashtagTimelineViewController(for: tag, mastodonController: navigationDelegate.apiController)
}
}
return SFSafariViewController(url: url)
}
func getViewController(forLinkAt point: CGPoint) -> UIViewController? {

View File

@ -12,6 +12,7 @@ import Pachyderm
class FollowNotificationGroupTableViewCell: UITableViewCell {
var delegate: TuskerNavigationDelegate?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var avatarStackView: UIStackView!
@IBOutlet weak var timestampLabel: UILabel!
@ -133,12 +134,13 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (content: {
let accountIDs = self.group.notificationIDs.compactMap(MastodonCache.notification(for:)).map { $0.account.id }
if accountIDs.count == 1 {
return ProfileTableViewController(accountID: accountIDs.first!)
return ProfileTableViewController(accountID: accountIDs.first!, mastodonController: mastodonController)
} else {
return AccountListTableViewController(accountIDs: accountIDs)
return AccountListTableViewController(accountIDs: accountIDs, mastodonController: mastodonController)
}
}, actions: {
return []

View File

@ -12,6 +12,7 @@ import Pachyderm
class FollowRequestNotificationTableViewCell: UITableViewCell {
var delegate: TuskerNavigationDelegate?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var stackView: UIStackView!
@IBOutlet weak var avatarImageView: UIImageView!
@ -89,7 +90,7 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
@IBAction func rejectButtonPressed() {
let request = Account.rejectFollowRequest(account)
MastodonController.run(request) { (response) in
mastodonController!.run(request) { (response) in
guard case let .success(relationship, _) = response else { fatalError() }
MastodonCache.add(relationship: relationship)
DispatchQueue.main.async {
@ -106,7 +107,7 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
@IBAction func acceptButtonPressed() {
let request = Account.authorizeFollowRequest(account)
MastodonController.run(request) { (response) in
mastodonController!.run(request) { (response) in
guard case let .success(relationship, _) = response else { fatalError() }
MastodonCache.add(relationship: relationship)
DispatchQueue.main.async {
@ -133,8 +134,9 @@ extension FollowRequestNotificationTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (content: {
return ProfileTableViewController(accountID: self.account.id)
return ProfileTableViewController(accountID: self.account.id, mastodonController: mastodonController)
}, actions: {
return []
})

View File

@ -16,6 +16,7 @@ protocol ProfileHeaderTableViewCellDelegate: TuskerNavigationDelegate {
class ProfileHeaderTableViewCell: UITableViewCell {
var delegate: ProfileHeaderTableViewCellDelegate?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var headerImageView: UIImageView!
@IBOutlet weak var avatarContainerView: UIView!
@ -82,7 +83,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
noteLabel.setTextFromHtml(account.note)
noteLabel.setEmojis(account.emojis)
if accountID != MastodonController.account.id {
if accountID != mastodonController!.account.id {
// don't show relationship label for the user's own account
if let relationship = MastodonCache.relationship(for: accountID) {
followsYouLabel.isHidden = !relationship.followedBy

View File

@ -15,11 +15,13 @@ protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
}
class BaseStatusTableViewCell: UITableViewCell {
var delegate: StatusTableViewCellDelegate? {
didSet {
contentLabel.navigationDelegate = delegate
}
}
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel!
@ -247,7 +249,7 @@ class BaseStatusTableViewCell: UITableViewCell {
let realStatus: Status = status.reblog ?? status
let request = (favorited ? Status.favourite : Status.unfavourite)(realStatus)
MastodonController.run(request) { response in
mastodonController!.run(request) { response in
DispatchQueue.main.async {
if case let .success(newStatus, _) = response {
self.favorited = newStatus.favourited ?? false
@ -272,7 +274,7 @@ class BaseStatusTableViewCell: UITableViewCell {
let realStatus: Status = status.reblog ?? status
let request = (reblogged ? Status.reblog : Status.unreblog)(realStatus)
MastodonController.run(request) { response in
mastodonController!.run(request) { response in
DispatchQueue.main.async {
if case let .success(newStatus, _) = response {
self.reblogged = newStatus.reblogged ?? false
@ -313,8 +315,13 @@ extension BaseStatusTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
if avatarImageView.frame.contains(location) {
return (content: { ProfileTableViewController(accountID: self.accountID)}, actions: { self.actionsForProfile(accountID: self.accountID) })
return (content: {
ProfileTableViewController(accountID: self.accountID, mastodonController: mastodonController)
}, actions: {
self.actionsForProfile(accountID: self.accountID)
})
} else if attachmentsView.frame.contains(location) {
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
if let attachmentView = attachmentsView.attachmentViews.allObjects.first(where: { $0.frame.contains(attachmentsViewLocation) }),

View File

@ -125,8 +125,9 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
}
override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy()) },
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy(), mastodonController: mastodonController) },
actions: { self.actionsForStatus(statusID: self.statusID) }
)
}
@ -142,6 +143,7 @@ extension TimelineStatusTableViewCell: SelectableTableViewCell {
extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? {
guard let mastodonController = mastodonController else { return nil }
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
let favoriteTitle: String
@ -158,7 +160,7 @@ extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1)
}
let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in
MastodonController.run(favoriteRequest, completion: { response in
mastodonController.run(favoriteRequest, completion: { response in
DispatchQueue.main.async {
guard case let .success(status, _) = response else {
completion(false)
@ -185,7 +187,7 @@ extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
reblogColor = tintColor
}
let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in
MastodonController.run(reblogRequest, completion: { response in
mastodonController.run(reblogRequest, completion: { response in
DispatchQueue.main.async {
guard case let .success(status, _) = response else {
completion(false)

View File

@ -13,6 +13,8 @@ import SwiftSoup
struct XCBActions {
// MARK: - Utils
private static var mastodonController: MastodonController { .shared }
private static func getMainTabBarController() -> MainTabBarViewController {
return (UIApplication.shared.delegate as! AppDelegate).window!.rootViewController as! MainTabBarViewController
}
@ -42,7 +44,7 @@ struct XCBActions {
}
} else if let searchQuery = request.arguments["statusURL"] {
let request = Client.search(query: searchQuery)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(results, _) = response,
let status = results.statuses.first {
MastodonCache.add(status: status)
@ -73,7 +75,7 @@ struct XCBActions {
}
} else if let searchQuery = request.arguments["accountURL"] {
let request = Client.search(query: searchQuery)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(results, _) = response {
if let account = results.accounts.first {
MastodonCache.add(account: account)
@ -91,7 +93,7 @@ struct XCBActions {
}
} else if let acct = request.arguments["acct"] {
let request = Client.searchForAccount(query: acct)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(accounts, _) = response {
if let account = accounts.first {
MastodonCache.add(account: account)
@ -118,7 +120,7 @@ struct XCBActions {
static func showStatus(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
getStatus(from: request, session: session) { (status) in
DispatchQueue.main.async {
let vc = ConversationTableViewController(for: status.id)
let vc = ConversationTableViewController(for: status.id, mastodonController: mastodonController)
show(vc)
}
}
@ -132,14 +134,14 @@ struct XCBActions {
var status = ""
if let mentioning = mentioning { status += mentioning }
if let text = text { status += text }
guard CharacterCounter.count(text: status) <= MastodonController.instance.maxStatusCharacters ?? 500 else {
guard CharacterCounter.count(text: status) <= mastodonController.instance.maxStatusCharacters ?? 500 else {
session.complete(with: .error, additionalData: [
"error": "Too many characters. Instance maximum is \(MastodonController.instance.maxStatusCharacters ?? 500)"
"error": "Too many characters. Instance maximum is \(mastodonController.instance.maxStatusCharacters ?? 500)"
])
return
}
let request = Client.createStatus(text: status, visibility: Preferences.shared.defaultPostVisibility)
MastodonController.run(request) { response in
mastodonController.run(request) { response in
if case let .success(status, _) = response {
session.complete(with: .success, additionalData: [
"statusURL": status.url?.absoluteString,
@ -152,7 +154,7 @@ struct XCBActions {
}
}
} else {
let compose = ComposeViewController(mentioningAcct: mentioning, text: text)
let compose = ComposeViewController(mentioningAcct: mentioning, text: text, mastodonController: mastodonController)
compose.xcbSession = session
let vc = UINavigationController(rootViewController: compose)
present(vc)
@ -199,7 +201,7 @@ struct XCBActions {
static func statusAction(request: @escaping (Status) -> Request<Status>, alertTitle: String, _ url: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
func performAction(status: Status, completion: ((Status) -> Void)?) {
MastodonController.run(request(status)) { (response) in
mastodonController.run(request(status)) { (response) in
if case let .success(status, _) = response {
MastodonCache.add(status: status)
completion?(status)
@ -219,7 +221,7 @@ struct XCBActions {
if silent ?? false {
performAction(status: status, completion: nil)
} else {
let vc = ConversationTableViewController(for: status.id)
let vc = ConversationTableViewController(for: status.id, mastodonController: mastodonController)
DispatchQueue.main.async {
show(vc)
}
@ -247,7 +249,7 @@ struct XCBActions {
static func showAccount(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
getAccount(from: request, session: session) { (account) in
DispatchQueue.main.async {
let vc = ProfileTableViewController(accountID: account.id)
let vc = ProfileTableViewController(accountID: account.id, mastodonController: mastodonController)
show(vc)
}
}
@ -269,7 +271,7 @@ struct XCBActions {
}
static func getCurrentUser(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
let account = MastodonController.account!
let account = mastodonController.account!
session.complete(with: .success, additionalData: [
"username": account.acct,
"displayName": account.displayName,
@ -285,7 +287,7 @@ struct XCBActions {
static func followUser(_ request: XCBRequest, _ session: XCBSession, _ silent: Bool?) {
func performAction(_ account: Account) {
let request = Account.follow(account.id)
MastodonController.run(request) { (response) in
mastodonController.run(request) { (response) in
if case let .success(relationship, _) = response {
MastodonCache.add(relationship: relationship)
session.complete(with: .success, additionalData: [
@ -303,7 +305,7 @@ struct XCBActions {
if silent ?? false {
performAction(account)
} else {
let vc = ProfileTableViewController(accountID: account.id)
let vc = ProfileTableViewController(accountID: account.id, mastodonController: mastodonController)
DispatchQueue.main.async {
show(vc)
}