Compare commits
2 Commits
e4f1309e2d
...
5ab22e742b
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 5ab22e742b | |
Shadowfacts | 4f655bb80a |
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class AccountListViewController: UIViewController {
|
class AccountListViewController: UIViewController, CollectionViewController {
|
||||||
typealias Item = String
|
typealias Item = String
|
||||||
|
|
||||||
private let mastodonController: MastodonController
|
private let mastodonController: MastodonController
|
||||||
private let accountIDs: [String]
|
private let accountIDs: [String]
|
||||||
|
|
||||||
private var collectionView: UICollectionView {
|
var collectionView: UICollectionView! {
|
||||||
view as! UICollectionView
|
view as! UICollectionView
|
||||||
}
|
}
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
|
@ -61,9 +61,7 @@ class AccountListViewController: UIViewController {
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
collectionView.indexPathsForSelectedItems?.forEach {
|
clearSelectionOnAppear(animated: animated)
|
||||||
collectionView.deselectItem(at: $0, animated: true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ import Pachyderm
|
||||||
import CoreData
|
import CoreData
|
||||||
import WebURLFoundationExtras
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
class ExploreViewController: UIViewController, UICollectionViewDelegate, CollectionViewController {
|
||||||
|
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
private var collectionView: UICollectionView!
|
var collectionView: UICollectionView!
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
|
|
||||||
private(set) var resultsController: SearchResultsViewController!
|
private(set) var resultsController: SearchResultsViewController!
|
||||||
|
@ -95,15 +95,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
// Can't use UICollectionViewController's builtin version of this because it requires
|
clearSelectionOnAppear(animated: animated)
|
||||||
// the collection view layout be passed into the constructor. Swipe actions for list collection views
|
|
||||||
// are created by passing a closure to the layout's configuration. This closure needs to capture
|
|
||||||
// `self`, so it can't be passed into the super constructor.
|
|
||||||
if let indexPaths = collectionView.indexPathsForSelectedItems {
|
|
||||||
for indexPath in indexPaths {
|
|
||||||
collectionView.deselectItem(at: indexPath, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionViewController {
|
class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionViewController, CollectionViewController {
|
||||||
|
|
||||||
weak var owner: ProfileViewController?
|
weak var owner: ProfileViewController?
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
@ -188,9 +188,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
collectionView.indexPathsForSelectedItems?.forEach {
|
clearSelectionOnAppear(animated: animated)
|
||||||
collectionView.deselectItem(at: $0, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
if case .notLoadedInitial = controller.state {
|
if case .notLoadedInitial = controller.state {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class StatusActionAccountListViewController: UIViewController {
|
class StatusActionAccountListViewController: UIViewController, CollectionViewController {
|
||||||
|
|
||||||
private let mastodonController: MastodonController
|
private let mastodonController: MastodonController
|
||||||
private let actionType: ActionType
|
private let actionType: ActionType
|
||||||
|
@ -20,8 +20,8 @@ class StatusActionAccountListViewController: UIViewController {
|
||||||
/// If `true`, a warning will be shown below the account list describing that the total favs/reblogs may be innacurate.
|
/// If `true`, a warning will be shown below the account list describing that the total favs/reblogs may be innacurate.
|
||||||
var showInacurateCountWarning = false
|
var showInacurateCountWarning = false
|
||||||
|
|
||||||
private var collectionView: UICollectionView {
|
var collectionView: UICollectionView! {
|
||||||
view as! UICollectionView
|
view as? UICollectionView
|
||||||
}
|
}
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
|
|
||||||
|
@ -120,9 +120,7 @@ class StatusActionAccountListViewController: UIViewController {
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
collectionView.indexPathsForSelectedItems?.forEach {
|
clearSelectionOnAppear(animated: animated)
|
||||||
collectionView.deselectItem(at: $0, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if accountIDs == nil {
|
if accountIDs == nil {
|
||||||
Task {
|
Task {
|
||||||
|
|
|
@ -210,9 +210,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
collectionView.indexPathsForSelectedItems?.forEach {
|
clearSelectionOnAppear(animated: animated)
|
||||||
collectionView.deselectItem(at: $0, animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if case .notLoadedInitial = controller.state {
|
if case .notLoadedInitial = controller.state {
|
||||||
if restoreState() {
|
if restoreState() {
|
||||||
|
|
|
@ -11,3 +11,23 @@ import UIKit
|
||||||
protocol CollectionViewController: UIViewController {
|
protocol CollectionViewController: UIViewController {
|
||||||
var collectionView: UICollectionView! { get }
|
var collectionView: UICollectionView! { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension CollectionViewController {
|
||||||
|
func clearSelectionOnAppear(animated: Bool) {
|
||||||
|
guard let indexPath = collectionView.indexPathsForSelectedItems?.first else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let transitionCoordinator {
|
||||||
|
transitionCoordinator.animate { context in
|
||||||
|
self.collectionView.deselectItem(at: indexPath, animated: true)
|
||||||
|
} completion: { context in
|
||||||
|
if context.isCancelled {
|
||||||
|
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [] /* UICollectionViewScrollPositionNone */)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
collectionView.deselectItem(at: indexPath, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import Sentry
|
||||||
|
|
||||||
struct ToastConfiguration {
|
struct ToastConfiguration {
|
||||||
var systemImageName: String?
|
var systemImageName: String?
|
||||||
|
@ -38,7 +39,7 @@ extension ToastConfiguration {
|
||||||
init(from error: Error, with title: String, in viewController: UIViewController, retryAction: ((ToastView) -> Void)?) {
|
init(from error: Error, with title: String, in viewController: UIViewController, retryAction: ((ToastView) -> Void)?) {
|
||||||
self.init(title: title)
|
self.init(title: title)
|
||||||
// localizedDescription is statically dispatched, so we need to call it after the downcast
|
// localizedDescription is statically dispatched, so we need to call it after the downcast
|
||||||
if let error = error as? Client.Error {
|
if let error = error as? Pachyderm.Client.Error {
|
||||||
self.subtitle = error.localizedDescription
|
self.subtitle = error.localizedDescription
|
||||||
self.systemImageName = error.systemImageName
|
self.systemImageName = error.systemImageName
|
||||||
self.longPressAction = { [unowned viewController] toast in
|
self.longPressAction = { [unowned viewController] toast in
|
||||||
|
@ -54,6 +55,35 @@ extension ToastConfiguration {
|
||||||
})
|
})
|
||||||
viewController.present(reporter, animated: true)
|
viewController.present(reporter, animated: true)
|
||||||
}
|
}
|
||||||
|
// TODO: this is a bizarre place to do this, but code path covers basically all errors
|
||||||
|
switch error.type {
|
||||||
|
case .invalidRequest, .invalidResponse, .invalidModel(_), .mastodonError(_):
|
||||||
|
SentrySDK.capture(error: error) { scope in
|
||||||
|
let crumb = Breadcrumb(level: .error, category: "error")
|
||||||
|
crumb.message = title
|
||||||
|
crumb.data = [
|
||||||
|
"request_method": error.requestMethod.name,
|
||||||
|
"request_endpoint": error.requestEndpoint.description,
|
||||||
|
]
|
||||||
|
switch error.type {
|
||||||
|
case .invalidRequest:
|
||||||
|
crumb.data!["error_type"] = "invalid_request"
|
||||||
|
case .invalidResponse:
|
||||||
|
crumb.data!["error_type"] = "invalid_response"
|
||||||
|
case .invalidModel(let error):
|
||||||
|
crumb.data!["error_type"] = "invalid_model"
|
||||||
|
crumb.data!["underlying_error"] = String(describing: error)
|
||||||
|
case .mastodonError(let error):
|
||||||
|
crumb.data!["error_type"] = "mastodon_error"
|
||||||
|
crumb.data!["underlying_error"] = error
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
scope.add(crumb)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.subtitle = error.localizedDescription
|
self.subtitle = error.localizedDescription
|
||||||
self.systemImageName = "exclamationmark.triangle"
|
self.systemImageName = "exclamationmark.triangle"
|
||||||
|
@ -73,7 +103,7 @@ extension ToastConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate extension Client.Error {
|
fileprivate extension Pachyderm.Client.Error {
|
||||||
var systemImageName: String {
|
var systemImageName: String {
|
||||||
switch type {
|
switch type {
|
||||||
case .networkError(_):
|
case .networkError(_):
|
||||||
|
|
Loading…
Reference in New Issue