Do the first parts of showing feed avatars.
This commit is contained in:
parent
1e528ee8b0
commit
dd05a24704
|
@ -15,6 +15,7 @@ final class FeaturedImageDownloader {
|
|||
private let imageDownloader: ImageDownloader
|
||||
private var articleURLToFeaturedImageURLCache = [String: String]()
|
||||
private var articleURLsWithNoFeaturedImage = Set<String>()
|
||||
private var urlsInProgress = Set<String>()
|
||||
|
||||
init(imageDownloader: ImageDownloader) {
|
||||
|
||||
|
@ -66,8 +67,15 @@ private extension FeaturedImageDownloader {
|
|||
|
||||
func findFeaturedImageURL(for articleURL: String) {
|
||||
|
||||
guard !urlsInProgress.contains(articleURL) else {
|
||||
return
|
||||
}
|
||||
urlsInProgress.insert(articleURL)
|
||||
|
||||
HTMLMetadataDownloader.downloadMetadata(for: articleURL) { (metadata) in
|
||||
|
||||
self.urlsInProgress.remove(articleURL)
|
||||
|
||||
guard let metadata = metadata else {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ public final class FeedIconDownloader {
|
|||
private let imageDownloader: ImageDownloader
|
||||
private var homePageToIconURLCache = [String: String]()
|
||||
private var homePagesWithNoIconURL = Set<String>()
|
||||
private var homePageDownloadsInProgress = Set<String>()
|
||||
private var urlsInProgress = Set<String>()
|
||||
private var cache = [Feed: NSImage]()
|
||||
|
||||
init(imageDownloader: ImageDownloader) {
|
||||
|
||||
|
@ -25,12 +26,22 @@ public final class FeedIconDownloader {
|
|||
|
||||
func icon(for feed: Feed) -> NSImage? {
|
||||
|
||||
if let cachedImage = cache[feed] {
|
||||
return cachedImage
|
||||
}
|
||||
|
||||
if let iconURL = feed.iconURL {
|
||||
return icon(forURL: iconURL)
|
||||
if let image = icon(forURL: iconURL) {
|
||||
cache[feed] = image
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
if let homePageURL = feed.homePageURL {
|
||||
return icon(forHomePageURL: homePageURL)
|
||||
if let image = icon(forHomePageURL: homePageURL) {
|
||||
cache[feed] = image
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -71,14 +82,14 @@ private extension FeedIconDownloader {
|
|||
|
||||
func findIconURLForHomePageURL(_ homePageURL: String) {
|
||||
|
||||
guard !homePageDownloadsInProgress.contains(homePageURL) else {
|
||||
guard !urlsInProgress.contains(homePageURL) else {
|
||||
return
|
||||
}
|
||||
homePageDownloadsInProgress.insert(homePageURL)
|
||||
urlsInProgress.insert(homePageURL)
|
||||
|
||||
HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (metadata) in
|
||||
|
||||
self.homePageDownloadsInProgress.remove(homePageURL)
|
||||
self.urlsInProgress.remove(homePageURL)
|
||||
guard let metadata = metadata else {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -36,36 +36,44 @@ struct TimelineCellAppearance {
|
|||
let boxLeftMargin: CGFloat
|
||||
|
||||
let gridColor: NSColor
|
||||
|
||||
|
||||
let avatarSize: NSSize
|
||||
let avatarMarginRight: CGFloat
|
||||
let avatarAdjustmentTop: CGFloat
|
||||
|
||||
init(theme: VSTheme, fontSize: FontSize) {
|
||||
|
||||
let actualFontSize = AppDefaults.actualFontSize(for: fontSize)
|
||||
|
||||
cellPadding = theme.edgeInsets(forKey: "MainWindow.Timeline.cell.padding")
|
||||
self.cellPadding = theme.edgeInsets(forKey: "MainWindow.Timeline.cell.padding")
|
||||
|
||||
feedNameColor = theme.color(forKey: "MainWindow.Timeline.cell.feedNameColor")
|
||||
feedNameFont = NSFont.systemFont(ofSize: actualFontSize)
|
||||
faviconFeedNameSpacing = theme.float(forKey: "MainWindow.Timeline.cell.faviconFeedNameSpacing")
|
||||
self.feedNameColor = theme.color(forKey: "MainWindow.Timeline.cell.feedNameColor")
|
||||
self.feedNameFont = NSFont.systemFont(ofSize: actualFontSize)
|
||||
self.faviconFeedNameSpacing = theme.float(forKey: "MainWindow.Timeline.cell.faviconFeedNameSpacing")
|
||||
|
||||
dateColor = theme.color(forKey: "MainWindow.Timeline.cell.dateColor")
|
||||
self.dateColor = theme.color(forKey: "MainWindow.Timeline.cell.dateColor")
|
||||
let actualDateFontSize = AppDefaults.actualFontSize(for: fontSize)
|
||||
dateFont = NSFont.systemFont(ofSize: actualDateFontSize)
|
||||
dateMarginLeft = theme.float(forKey: "MainWindow.Timeline.cell.dateMarginLeft")
|
||||
self.dateFont = NSFont.systemFont(ofSize: actualDateFontSize)
|
||||
self.dateMarginLeft = theme.float(forKey: "MainWindow.Timeline.cell.dateMarginLeft")
|
||||
|
||||
titleColor = theme.color(forKey: "MainWindow.Timeline.cell.titleColor")
|
||||
titleFont = NSFont.systemFont(ofSize: actualFontSize, weight: NSFont.Weight.bold)
|
||||
titleBottomMargin = theme.float(forKey: "MainWindow.Timeline.cell.titleMarginBottom")
|
||||
self.titleColor = theme.color(forKey: "MainWindow.Timeline.cell.titleColor")
|
||||
self.titleFont = NSFont.systemFont(ofSize: actualFontSize, weight: NSFont.Weight.bold)
|
||||
self.titleBottomMargin = theme.float(forKey: "MainWindow.Timeline.cell.titleMarginBottom")
|
||||
|
||||
textColor = theme.color(forKey: "MainWindow.Timeline.cell.textColor")
|
||||
textFont = NSFont.systemFont(ofSize: actualFontSize)
|
||||
self.textColor = theme.color(forKey: "MainWindow.Timeline.cell.textColor")
|
||||
self.textFont = NSFont.systemFont(ofSize: actualFontSize)
|
||||
|
||||
unreadCircleColor = theme.color(forKey: "MainWindow.Timeline.cell.unreadCircleColor")
|
||||
unreadCircleDimension = theme.float(forKey: "MainWindow.Timeline.cell.unreadCircleDimension")
|
||||
unreadCircleMarginRight = theme.float(forKey: "MainWindow.Timeline.cell.unreadCircleMarginRight")
|
||||
self.unreadCircleColor = theme.color(forKey: "MainWindow.Timeline.cell.unreadCircleColor")
|
||||
self.unreadCircleDimension = theme.float(forKey: "MainWindow.Timeline.cell.unreadCircleDimension")
|
||||
self.unreadCircleMarginRight = theme.float(forKey: "MainWindow.Timeline.cell.unreadCircleMarginRight")
|
||||
|
||||
boxLeftMargin = cellPadding.left + unreadCircleDimension + unreadCircleMarginRight
|
||||
self.gridColor = theme.colorWithAlpha(forKey: "MainWindow.Timeline.gridColor")
|
||||
|
||||
self.avatarSize = theme.size(forKey: "MainWindow.Timeline.cell.avatar")
|
||||
self.avatarMarginRight = theme.float(forKey: "MainWindow.Timeline.cell.avatarMarginRight")
|
||||
self.avatarAdjustmentTop = theme.float(forKey: "MainWindow.Timeline.cell.avatarAdjustmentTop")
|
||||
|
||||
gridColor = theme.colorWithAlpha(forKey: "MainWindow.Timeline.gridColor")
|
||||
self.boxLeftMargin = self.cellPadding.left + self.unreadCircleDimension + self.unreadCircleMarginRight + self.avatarSize.width + self.avatarMarginRight
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,10 @@ struct TimelineCellLayout {
|
|||
height = NSMaxY(feedNameRect)
|
||||
}
|
||||
height = height + paddingBottom
|
||||
|
||||
|
||||
let heightOfImage = avatarImageRect.maxY + paddingBottom
|
||||
height = max(height, heightOfImage)
|
||||
|
||||
self.height = height
|
||||
}
|
||||
}
|
||||
|
@ -59,11 +62,8 @@ private func rectForDate(_ cellData: TimelineCellData, _ width: CGFloat, _ appea
|
|||
r.origin.y = NSMaxY(titleRect) + appearance.titleBottomMargin
|
||||
r.origin.x = appearance.boxLeftMargin
|
||||
|
||||
r.size.width = width - (r.origin.x + appearance.cellPadding.right)
|
||||
if r.size.width < 15 {
|
||||
return NSZeroRect
|
||||
}
|
||||
|
||||
r.size.width = max(width - (r.origin.x + appearance.cellPadding.right), 0.0)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,8 @@ private func rectsForTitle(_ cellData: TimelineCellData, _ width: CGFloat, _ app
|
|||
|
||||
let measurements = renderer.measurements(forWidth: textWidth)
|
||||
r.size = NSSize(width: textWidth, height: CGFloat(measurements.height))
|
||||
|
||||
r.size.width = max(r.size.width, 0.0)
|
||||
|
||||
var rline1 = r
|
||||
rline1.size.height = CGFloat(measurements.heightOfFirstLine)
|
||||
|
||||
|
@ -139,10 +140,14 @@ private func rectForUnreadIndicator(_ cellData: TimelineCellData, _ appearance:
|
|||
return r
|
||||
}
|
||||
|
||||
private func rectForAvatar(_ cellData: TimelineCellData, _ appearance: TimelineCellAppearance, _ unreadIndictarRect: NSRect) -> NSRect {
|
||||
private func rectForAvatar(_ cellData: TimelineCellData, _ appearance: TimelineCellAppearance, _ titleLine1Rect: NSRect) -> NSRect {
|
||||
|
||||
// TODO
|
||||
return NSRect.zero
|
||||
var r = NSRect.zero
|
||||
r.size = appearance.avatarSize
|
||||
r.origin.x = appearance.cellPadding.left + appearance.unreadCircleDimension + appearance.unreadCircleMarginRight
|
||||
r.origin.y = titleLine1Rect.minY + appearance.avatarAdjustmentTop
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func timelineCellLayout(_ width: CGFloat, cellData: TimelineCellData, appearance: TimelineCellAppearance) -> TimelineCellLayout {
|
||||
|
@ -153,7 +158,7 @@ func timelineCellLayout(_ width: CGFloat, cellData: TimelineCellData, appearance
|
|||
let feedNameRect = rectForFeedName(cellData, width, appearance, titleRect)
|
||||
let faviconRect = rectForFavicon(cellData, appearance, feedNameRect)
|
||||
let unreadIndicatorRect = rectForUnreadIndicator(cellData, appearance, titleLine1Rect)
|
||||
let avatarImageRect = rectForAvatar(cellData, appearance, unreadIndicatorRect)
|
||||
let avatarImageRect = rectForAvatar(cellData, appearance, titleLine1Rect)
|
||||
|
||||
return TimelineCellLayout(width: width, faviconRect: faviconRect, feedNameRect: feedNameRect, dateRect: dateRect, titleRect: titleRect, unreadIndicatorRect: unreadIndicatorRect, avatarImageRect: avatarImageRect, paddingBottom: appearance.cellPadding.bottom)
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ class TimelineTableCellView: NSTableCellView {
|
|||
unreadIndicatorView.rs_setFrameIfNotEqual(layoutRects.unreadIndicatorRect)
|
||||
dateView.rs_setFrameIfNotEqual(layoutRects.dateRect)
|
||||
feedNameView.rs_setFrameIfNotEqual(layoutRects.feedNameRect)
|
||||
avatarImageView.rs_setFrameIfNotEqual(layoutRects.avatarImageRect)
|
||||
}
|
||||
|
||||
override func updateLayer() {
|
||||
|
@ -173,12 +174,26 @@ class TimelineTableCellView: NSTableCellView {
|
|||
}
|
||||
}
|
||||
|
||||
private func updateAvatar() {
|
||||
|
||||
if let image = cellData.avatar {
|
||||
if avatarImageView.image !== image {
|
||||
avatarImageView.image = image
|
||||
}
|
||||
avatarImageView.isHidden = false
|
||||
}
|
||||
else {
|
||||
avatarImageView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSubviews() {
|
||||
|
||||
updateTitleView()
|
||||
updateDateView()
|
||||
updateFeedNameView()
|
||||
updateUnreadIndicator()
|
||||
updateAvatar()
|
||||
}
|
||||
|
||||
private func updateAppearance() {
|
||||
|
|
|
@ -94,11 +94,13 @@
|
|||
<key>unreadCircleMarginRight</key>
|
||||
<integer>8</integer>
|
||||
<key>avatarHeight</key>
|
||||
<string>64</string>
|
||||
<integer>42</integer>
|
||||
<key>avatarWidth</key>
|
||||
<string>64</string>
|
||||
<integer>42</integer>
|
||||
<key>avatarMarginRight</key>
|
||||
<string>8</string>
|
||||
<integer>8</integer>
|
||||
<key>avatarAdjustmentTop</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
|
Loading…
Reference in New Issue