Fix crash when fetching recommended instances fails

This commit is contained in:
Shadowfacts 2021-08-12 19:36:28 -04:00
parent 1d79918a94
commit 85e1e131f6
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
1 changed files with 72 additions and 21 deletions

View File

@ -50,6 +50,11 @@ class InstanceSelectorTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// disable transparent background when scrolled to top because it gets weird with animating table items in and out
let appearance = UINavigationBarAppearance()
appearance.configureWithDefaultBackground()
navigationItem.scrollEdgeAppearance = appearance
tableView.register(UINib(nibName: "InstanceTableViewCell", bundle: .main), forCellReuseIdentifier: instanceCell)
tableView.rowHeight = UITableView.automaticDimension
@ -120,14 +125,18 @@ class InstanceSelectorTableViewController: UITableViewController {
client.run(request) { (response) in
var snapshot = self.dataSource.snapshot()
if snapshot.indexOfSection(.selected) != nil {
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .selected))
snapshot.deleteSections([.selected])
}
if case let .success(instance, _) = response {
if !snapshot.sectionIdentifiers.contains(.selected) {
if snapshot.indexOfSection(.recommendedInstances) != nil {
snapshot.insertSections([.selected], beforeSection: .recommendedInstances)
} else {
snapshot.appendSections([.selected])
}
snapshot.appendItems([.selected(url, instance)], toSection: .selected)
DispatchQueue.main.async {
self.dataSource.apply(snapshot)
}
@ -137,14 +146,47 @@ class InstanceSelectorTableViewController: UITableViewController {
private func loadRecommendedInstances() {
InstanceSelector.getInstances(category: nil) { (response) in
guard case let .success(instances, _) = response else { fatalError() }
self.recommendedInstances = instances
self.filterRecommendedResults()
DispatchQueue.main.async {
switch response {
case let .failure(error):
self.showRecommendationsError(error)
case let .success(instances, _):
self.recommendedInstances = instances
self.filterRecommendedResults()
}
}
}
}
func filterRecommendedResults() {
private func showRecommendationsError(_ error: Client.Error) {
let footer = UITableViewHeaderFooterView()
footer.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .secondaryLabel
label.textAlignment = .center
label.font = .boldSystemFont(ofSize: 17)
label.numberOfLines = 0
label.text = "Could not fetch suggested instances: \(error.localizedDescription)"
footer.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalToSystemSpacingAfter: footer.contentView.leadingAnchor, multiplier: 1),
footer.contentView.trailingAnchor.constraint(equalToSystemSpacingAfter: label.trailingAnchor, multiplier: 1),
label.topAnchor.constraint(equalTo: footer.contentView.topAnchor, constant: 8),
label.bottomAnchor.constraint(equalTo: footer.contentView.bottomAnchor, constant: 8),
])
let fittingSize = CGSize(width: tableView.bounds.width - (tableView.safeAreaInsets.left + tableView.safeAreaInsets.right), height: 0)
let size = footer.systemLayoutSizeFitting(fittingSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
footer.frame = CGRect(origin: .zero, size: size)
tableView.tableFooterView = footer
}
private func filterRecommendedResults() {
let filteredInstances: [InstanceSelector.Instance]
if let currentQuery = currentQuery, !currentQuery.isEmpty {
filteredInstances = recommendedInstances.filter {
@ -155,12 +197,20 @@ class InstanceSelectorTableViewController: UITableViewController {
}
var snapshot = self.dataSource.snapshot()
snapshot.deleteSections([.recommendedInstances])
snapshot.appendSections([.recommendedInstances])
snapshot.appendItems(filteredInstances.map { Item.recommended($0) }, toSection: .recommendedInstances)
DispatchQueue.main.async {
self.dataSource.apply(snapshot)
if snapshot.indexOfSection(.recommendedInstances) != nil {
let toRemove = snapshot.itemIdentifiers(inSection: .recommendedInstances).filter {
if case .recommended(_) = $0 {
return true
} else {
return false
}
}
snapshot.deleteItems(toRemove)
} else {
snapshot.appendSections([.recommendedInstances])
}
snapshot.appendItems(filteredInstances.map { Item.recommended($0) }, toSection: .recommendedInstances)
self.dataSource.apply(snapshot)
}
// MARK: - Table view delegate
@ -194,29 +244,30 @@ extension InstanceSelectorTableViewController {
case recommended(InstanceSelector.Instance)
static func ==(lhs: Item, rhs: Item) -> Bool {
if case let .selected(url, instance) = lhs,
case let .selected(otherUrl, other) = rhs {
return url == otherUrl && instance.uri == other.uri
} else if case let .recommended(instance) = lhs,
case let .recommended(other) = rhs {
return instance.domain == other.domain
switch (lhs, rhs) {
case let (.selected(urlA, instanceA), .selected(urlB, instanceB)):
return urlA == urlB && instanceA.uri == instanceB.uri
case let (.recommended(a), .recommended(b)):
return a.domain == b.domain
default:
return false
}
return false
}
func hash(into hasher: inout Hasher) {
switch self {
case let .selected(url, instance):
hasher.combine(Section.selected)
hasher.combine(0)
hasher.combine(url)
hasher.combine(instance.uri)
case let .recommended(instance):
hasher.combine(Section.recommendedInstances)
hasher.combine(1)
hasher.combine(instance.domain)
}
}
}
class DataSource: UITableViewDiffableDataSource<Section, Item> {
}
}