From 3207ebf6a5c13d918fb67dbece402531374acc1e Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 21 May 2019 05:42:40 -0500 Subject: [PATCH] Implement default timeline avatar. Issue #641 --- Mac/{AppImages.swift => AppAssets.swift} | 8 +++- .../FeedInspectorViewController.swift | 2 +- .../SharingServicePickerDelegate.swift | 2 +- Mac/MainWindow/Sidebar/Cell/SidebarCell.swift | 2 +- .../Timeline/Cell/TimelineTableCellView.swift | 4 +- .../Timeline/TimelineViewController.swift | 16 +++++--- .../Accounts/AccountsAddViewController.swift | 4 +- .../AccountsPreferencesViewController.swift | 2 +- .../Contents.json | 15 +++++++ .../faviconTemplateImage.pdf | Bin 0 -> 5858 bytes NetNewsWire.xcodeproj/project.pbxproj | 14 +++---- Shared/Data/SmallIconProvider.swift | 2 +- Shared/Favicons/FaviconGenerator.swift | 2 +- iOS/AppAssets.swift | 12 +++--- iOS/Extensions/UIImage-Extensions.swift | 38 ------------------ submodules/RSCore | 2 +- 16 files changed, 55 insertions(+), 70 deletions(-) rename Mac/{AppImages.swift => AppAssets.swift} (84%) create mode 100644 Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/Contents.json create mode 100644 Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/faviconTemplateImage.pdf delete mode 100644 iOS/Extensions/UIImage-Extensions.swift diff --git a/Mac/AppImages.swift b/Mac/AppAssets.swift similarity index 84% rename from Mac/AppImages.swift rename to Mac/AppAssets.swift index f48b8bec4..86503c6eb 100644 --- a/Mac/AppImages.swift +++ b/Mac/AppAssets.swift @@ -1,5 +1,5 @@ // -// AppImages.swift +// AppAssets.swift // NetNewsWire // // Created by Brent Simmons on 2/17/18. @@ -14,7 +14,7 @@ extension NSImage.Name { static let timelineStar = NSImage.Name("timelineStar") } -struct AppImages { +struct AppAssets { static var genericFeedImage: RSImage? = { let path = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/BookmarkIcon.icns" @@ -34,4 +34,8 @@ struct AppImages { return RSImage(named: "accountFeedbin") }() + static var faviconTemplateImage: RSImage = { + return RSImage(named: "faviconTemplateImage")! + }() + } diff --git a/Mac/Inspector/FeedInspectorViewController.swift b/Mac/Inspector/FeedInspectorViewController.swift index 64bd3d9e7..394dddd8c 100644 --- a/Mac/Inspector/FeedInspectorViewController.swift +++ b/Mac/Inspector/FeedInspectorViewController.swift @@ -110,7 +110,7 @@ private extension FeedInspectorViewController { return } - imageView?.image = AppImages.genericFeedImage + imageView?.image = AppAssets.genericFeedImage } func updateName() { diff --git a/Mac/MainWindow/SharingServicePickerDelegate.swift b/Mac/MainWindow/SharingServicePickerDelegate.swift index 06f53ed74..0611aeb63 100644 --- a/Mac/MainWindow/SharingServicePickerDelegate.swift +++ b/Mac/MainWindow/SharingServicePickerDelegate.swift @@ -42,7 +42,7 @@ import RSCore return nil } - let image = sendToCommand.image ?? AppImages.genericFeedImage ?? NSImage() + let image = sendToCommand.image ?? AppAssets.genericFeedImage ?? NSImage() return NSSharingService(title: sendToCommand.title, image: image, alternateImage: nil) { sendToCommand.sendObject(object, selectedText: nil) } diff --git a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift index 2a36ba0fb..3df7366e6 100644 --- a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift +++ b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift @@ -81,7 +81,7 @@ class SidebarCell : NSTableCellView { }() private let faviconImageView: NSImageView = { - let image = AppImages.genericFeedImage + let image = AppAssets.genericFeedImage let imageView = image != nil ? NSImageView(image: image!) : NSImageView(frame: NSRect.zero) imageView.animates = false imageView.imageAlignment = .alignCenter diff --git a/Mac/MainWindow/Timeline/Cell/TimelineTableCellView.swift b/Mac/MainWindow/Timeline/Cell/TimelineTableCellView.swift index 1f62993d6..ea4dcadee 100644 --- a/Mac/MainWindow/Timeline/Cell/TimelineTableCellView.swift +++ b/Mac/MainWindow/Timeline/Cell/TimelineTableCellView.swift @@ -19,14 +19,14 @@ class TimelineTableCellView: NSTableCellView { private let feedNameView = TimelineTableCellView.singleLineTextField() private lazy var avatarImageView: NSImageView = { - let imageView = TimelineTableCellView.imageView(with: AppImages.genericFeedImage, scaling: .scaleNone) + let imageView = TimelineTableCellView.imageView(with: AppAssets.genericFeedImage, scaling: .scaleNone) imageView.imageAlignment = .alignTop imageView.imageScaling = .scaleProportionallyDown imageView.wantsLayer = true return imageView }() - private let starView = TimelineTableCellView.imageView(with: AppImages.timelineStar, scaling: .scaleNone) + private let starView = TimelineTableCellView.imageView(with: AppAssets.timelineStar, scaling: .scaleNone) private lazy var textFields = { return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView] diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index b0b1c5797..0bfec37f8 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -702,10 +702,7 @@ extension TimelineViewController: NSTableViewDelegate { private func configureTimelineCell(_ cell: TimelineTableCellView, article: Article) { cell.objectValue = article - var avatar = avatarFor(article) - if avatar == nil, let feed = article.feed { - avatar = appDelegate.faviconDownloader.favicon(for: feed) - } + let avatar = avatarFor(article) let featuredImage = featuredImageFor(article) cell.cellData = TimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, avatar: avatar, showAvatar: showAvatars, featuredImage: featuredImage) @@ -715,6 +712,7 @@ extension TimelineViewController: NSTableViewDelegate { if !showAvatars { return nil } + if let authors = article.authors { for author in authors { if let image = avatarForAuthor(author) { @@ -727,7 +725,15 @@ extension TimelineViewController: NSTableViewDelegate { return nil } - return appDelegate.feedIconDownloader.icon(for: feed) + if let feedIcon = appDelegate.feedIconDownloader.icon(for: feed) { + return feedIcon + } + + if let favicon = appDelegate.faviconDownloader.favicon(for: feed) { + return favicon + } + + return FaviconGenerator.favicon(feed) } private func avatarForAuthor(_ author: Author) -> NSImage? { diff --git a/Mac/Preferences/Accounts/AccountsAddViewController.swift b/Mac/Preferences/Accounts/AccountsAddViewController.swift index a77a4bdd3..6c3a4158f 100644 --- a/Mac/Preferences/Accounts/AccountsAddViewController.swift +++ b/Mac/Preferences/Accounts/AccountsAddViewController.swift @@ -59,10 +59,10 @@ extension AccountsAddViewController: NSTableViewDelegate { switch row { case 0: cell.accountNameLabel?.stringValue = Account.defaultLocalAccountName - cell.accountImageView?.image = AppImages.accountLocal + cell.accountImageView?.image = AppAssets.accountLocal case 1: cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin") - cell.accountImageView?.image = AppImages.accountFeedbin + cell.accountImageView?.image = AppAssets.accountFeedbin default: break } diff --git a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift index 53f86e211..8cf4cde31 100644 --- a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift +++ b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift @@ -100,7 +100,7 @@ extension AccountsPreferencesViewController: NSTableViewDelegate { cell.textField?.stringValue = account.nameForDisplay switch account.type { case .onMyMac: - cell.imageView?.image = AppImages.accountLocal + cell.imageView?.image = AppAssets.accountLocal case .feedbin: cell.imageView?.image = NSImage(named: "accountFeedbin") default: diff --git a/Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/Contents.json b/Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/Contents.json new file mode 100644 index 000000000..22efb41ab --- /dev/null +++ b/Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "faviconTemplateImage.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/faviconTemplateImage.pdf b/Mac/Resources/Assets.xcassets/faviconTemplateImage.imageset/faviconTemplateImage.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d6bcb5b69b270394b8e33a787388fd7a3dddd7ee GIT binary patch literal 5858 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 UIImage? { - - let maskImage = cgImage! - - let width = size.width - let height = size.height - let bounds = CGRect(x: 0, y: 0, width: width, height: height) - - let colorSpace = CGColorSpaceCreateDeviceRGB() - let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) - let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! - - context.clip(to: bounds, mask: maskImage) - context.setFillColor(color.cgColor) - context.fill(bounds) - - if let cgImage = context.makeImage() { - let coloredImage = UIImage(cgImage: cgImage) - return coloredImage - } else { - return nil - } - - } - -} diff --git a/submodules/RSCore b/submodules/RSCore index 44f046dca..b65da2c52 160000 --- a/submodules/RSCore +++ b/submodules/RSCore @@ -1 +1 @@ -Subproject commit 44f046dca9901e50bc7132fb8d296d38b69673d9 +Subproject commit b65da2c5286c6bc342064c738dd351750ea6fe12