Fix profile header image not showing up on first load

The issue occurred because the profile header would kick off a request
upon loading, then the profile table would request the initial set of
statuses shortly thereafter which would result in reloadData being called
which would cancel the request without removing the group, so the request
generated by the newly-reloaded header cell would attach a callback to
the cancelled request, resulting in the header image never displaying.
This commit is contained in:
Shadowfacts 2020-06-15 22:34:42 -04:00
parent 36326e4469
commit d27bddb2ca
2 changed files with 32 additions and 26 deletions

View File

@ -18,7 +18,7 @@ class ImageCache {
let cache: Cache<Data> let cache: Cache<Data>
var requests = [URL: RequestGroup]() private var groups = [URL: RequestGroup]()
init(name: String, memoryExpiry expiry: Expiry) { init(name: String, memoryExpiry expiry: Expiry) {
let storage = MemoryStorage<Data>(config: MemoryConfig(expiry: expiry)) let storage = MemoryStorage<Data>(config: MemoryConfig(expiry: expiry))
@ -43,14 +43,18 @@ class ImageCache {
completion?(data) completion?(data)
return nil return nil
} else { } else {
if let completion = completion, let group = requests[url] { if let completion = completion, let group = groups[url] {
return group.addCallback(completion) return group.addCallback(completion)
} else { } else {
let group = RequestGroup(url: url) let group = RequestGroup(url: url) { (data) in
let request = group.addCallback(completion) if let data = data {
group.run { (data) in try? self.cache.setObject(data, forKey: key)
try? self.cache.setObject(data, forKey: key) }
self.groups.removeValue(forKey: url)
} }
groups[url] = group
let request = group.addCallback(completion)
group.run()
return request return request
} }
} }
@ -61,29 +65,30 @@ class ImageCache {
} }
func cancelWithoutCallback(_ url: URL) { func cancelWithoutCallback(_ url: URL) {
requests[url]?.cancelWithoutCallback() groups[url]?.cancelWithoutCallback()
} }
class RequestGroup { private class RequestGroup {
let url: URL let url: URL
private let onFinished: (Data?) -> Void
private var task: URLSessionDataTask? private var task: URLSessionDataTask?
private var requests = [Request]() private var requests = [Request]()
init(url: URL) { init(url: URL, onFinished: @escaping (Data?) -> Void) {
self.url = url self.url = url
self.onFinished = onFinished
} }
deinit { deinit {
task?.cancel() task?.cancel()
} }
func run(cache: @escaping (Data) -> Void) { func run() {
task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard error == nil, let data = data else { guard error == nil, let data = data else {
self.complete(with: nil) self.complete(with: nil)
return return
} }
cache(data)
self.complete(with: data) self.complete(with: data)
}) })
task!.resume() task!.resume()
@ -123,11 +128,12 @@ class ImageCache {
callback(data) callback(data)
} }
} }
self.onFinished(data)
} }
} }
class Request { class Request {
weak var group: RequestGroup? private weak var group: RequestGroup?
private(set) var callback: ((Data?) -> Void)? private(set) var callback: ((Data?) -> Void)?
private(set) var cancelled: Bool = false private(set) var cancelled: Bool = false

View File

@ -16,20 +16,8 @@ class ProfileTableViewController: EnhancedTableViewController {
var accountID: String! var accountID: String!
var pinnedStatuses: [(id: String, state: StatusState)] = [] { var pinnedStatuses: [(id: String, state: StatusState)] = []
didSet { var timelineSegments: [[(id: String, state: StatusState)]] = []
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
var older: RequestRange? var older: RequestRange?
var newer: RequestRange? var newer: RequestRange?
@ -124,6 +112,12 @@ class ProfileTableViewController: EnhancedTableViewController {
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
self.pinnedStatuses = statuses.map { ($0.id, .unknown) } self.pinnedStatuses = statuses.map { ($0.id, .unknown) }
let indexPaths = (0..<statuses.count).map { IndexPath(row: $0, section: 1) }
DispatchQueue.main.async {
UIView.performWithoutAnimation {
self.tableView.insertRows(at: indexPaths, with: .none)
}
}
} }
} }
@ -135,6 +129,12 @@ class ProfileTableViewController: EnhancedTableViewController {
self.older = pagination?.older self.older = pagination?.older
self.newer = pagination?.newer self.newer = pagination?.newer
DispatchQueue.main.async {
UIView.performWithoutAnimation {
self.tableView.insertSections(IndexSet(integer: 2), with: .none)
}
}
} }
} }
} }