Fix bug where user avatars wouldn’t always show up in the timeline view promptly.

This commit is contained in:
Brent Simmons 2018-01-05 13:22:16 -08:00
parent 665561e5eb
commit 45f3f49a1f
3 changed files with 39 additions and 13 deletions

View File

@ -11,17 +11,19 @@ import Data
extension Notification.Name {
static let AvatarDidBecomeAvailable = Notification.Name("AvatarDidBecomeAvailableNotification") // UserInfoKey.author
static let AvatarDidBecomeAvailable = Notification.Name("AvatarDidBecomeAvailableNotification") // UserInfoKey.imageURL (which is an avatarURL)
}
final class AuthorAvatarDownloader {
private let imageDownloader: ImageDownloader
private var cache = [String: NSImage]() // avatarURL: NSImage
private var waitingForAvatarURLs = Set<String>()
init(imageDownloader: ImageDownloader) {
self.imageDownloader = imageDownloader
NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: imageDownloader)
}
func image(for author: Author) -> NSImage? {
@ -33,21 +35,49 @@ final class AuthorAvatarDownloader {
return cachedImage
}
if let image = imageDownloader.image(for: avatarURL) {
cache[avatarURL] = image
postAvatarDidBecomeAvailableNotification(author)
handleImageDidBecomeAvailable(avatarURL, image)
return image
}
else {
waitingForAvatarURLs.insert(avatarURL)
}
return nil
}
@objc func imageDidBecomeAvailable(_ note: Notification) {
guard let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
return
}
guard waitingForAvatarURLs.contains(avatarURL) else {
return
}
guard let image = imageDownloader.image(for: avatarURL) else {
return
}
handleImageDidBecomeAvailable(avatarURL, image)
}
}
private extension AuthorAvatarDownloader {
func postAvatarDidBecomeAvailableNotification(_ author: Author) {
func handleImageDidBecomeAvailable(_ avatarURL: String, _ image: NSImage) {
if cache[avatarURL] == nil {
cache[avatarURL] = image
}
if waitingForAvatarURLs.contains(avatarURL) {
waitingForAvatarURLs.remove(avatarURL)
postAvatarDidBecomeAvailableNotification(avatarURL)
}
}
func postAvatarDidBecomeAvailableNotification(_ avatarURL: String) {
DispatchQueue.main.async {
let userInfo: [AnyHashable: Any] = [UserInfoKey.author: author]
NotificationCenter.default.post(name: .AvatarDidBecomeAvailable, object: self, userInfo: userInfo)
NotificationCenter.default.post(name: .AvatarDidBecomeAvailable, object: self, userInfo: [UserInfoKey.url: avatarURL])
}
}
}

View File

@ -12,7 +12,7 @@ import RSWeb
extension Notification.Name {
static let ImageDidBecomeAvailable = Notification.Name("ImageDidBecomeAvailableNotification") // ImageDownloader.UserInfoKey.imageURL
static let ImageDidBecomeAvailable = Notification.Name("ImageDidBecomeAvailableNotification") // UserInfoKey.url
}
final class ImageDownloader {
@ -24,10 +24,6 @@ final class ImageDownloader {
private var urlsInProgress = Set<String>()
private var badURLs = Set<String>() // That return a 404 or whatever. Just skip them in the future.
struct UserInfoKey {
static let imageURL = "imageURL"
}
init(folder: String) {
self.folder = folder
@ -135,6 +131,6 @@ private extension ImageDownloader {
func postImageDidBecomeAvailableNotification(_ url: String) {
NotificationCenter.default.post(name: .ImageDidBecomeAvailable, object: self, userInfo: [UserInfoKey.imageURL: url])
NotificationCenter.default.post(name: .ImageDidBecomeAvailable, object: self, userInfo: [UserInfoKey.url: url])
}
}

View File

@ -327,7 +327,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
@objc func avatarDidBecomeAvailable(_ note: Notification) {
guard let author = note.userInfo?[UserInfoKey.author] as? Author, let avatarURL = author.avatarURL else {
guard let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
return
}
let articlesToReload = articles.filter { (article) -> Bool in