Show byline in timeline for single feed timelines when available.

This commit is contained in:
Maurice Parker 2020-04-18 07:53:56 -05:00
parent 67434c2d0b
commit 1778a270d6
10 changed files with 91 additions and 45 deletions

View File

@ -15,14 +15,15 @@ struct TimelineCellData {
let text: String
let dateString: String
let feedName: String
let showFeedName: Bool
let byline: String
let showFeedName: TimelineShowFeedName
let iconImage: IconImage? // feed icon, user avatar, or favicon
let showIcon: Bool // Make space even when icon is nil
let featuredImage: NSImage? // image from within the article
let read: Bool
let starred: Bool
init(article: Article, showFeedName: Bool, feedName: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: NSImage?) {
init(article: Article, showFeedName: TimelineShowFeedName, feedName: String?, byline: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: NSImage?) {
self.title = ArticleStringFormatter.truncatedTitle(article)
self.text = ArticleStringFormatter.truncatedSummary(article)
@ -31,10 +32,15 @@ struct TimelineCellData {
if let feedName = feedName {
self.feedName = ArticleStringFormatter.truncatedFeedName(feedName)
}
else {
} else {
self.feedName = ""
}
if let byline = byline {
self.byline = byline
} else {
self.byline = ""
}
self.showFeedName = showFeedName
@ -51,7 +57,8 @@ struct TimelineCellData {
self.text = ""
self.dateString = ""
self.feedName = ""
self.showFeedName = false
self.byline = ""
self.showFeedName = .none
self.showIcon = false
self.iconImage = nil
self.featuredImage = nil

View File

@ -171,7 +171,7 @@ private extension TimelineCellLayout {
}
static func rectForFeedName(_ textBoxRect: NSRect, _ dateRect: NSRect, _ appearance: TimelineCellAppearance, _ cellData: TimelineCellData) -> NSRect {
if !cellData.showFeedName {
if cellData.showFeedName == .none {
return NSZeroRect
}

View File

@ -248,11 +248,14 @@ private extension TimelineTableCellView {
}
func updateFeedNameView() {
if cellData.showFeedName {
switch cellData.showFeedName {
case .byline:
showView(feedNameView)
updateTextFieldText(feedNameView, cellData.byline)
case .feed:
showView(feedNameView)
updateTextFieldText(feedNameView, cellData.feedName)
}
else {
case .none:
hideView(feedNameView)
}
}

View File

@ -18,6 +18,12 @@ protocol TimelineDelegate: class {
func timelineInvalidatedRestorationState(_: TimelineViewController)
}
enum TimelineShowFeedName {
case none
case byline
case feed
}
final class TimelineViewController: NSViewController, UndoableCommandRunner, UnreadCountProvider {
@IBOutlet var tableView: TimelineTableView!
@ -41,23 +47,11 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
didSet {
if !representedObjectArraysAreEqual(oldValue, representedObjects) {
unreadCount = 0
if let representedObjects = representedObjects {
if representedObjects.count == 1 && representedObjects.first is WebFeed {
showFeedNames = false
}
else {
showFeedNames = true
}
}
else {
showFeedNames = false
}
selectionDidChange(nil)
if showsSearchResults {
fetchAndReplaceArticlesAsync()
}
else {
} else {
fetchAndReplaceArticlesSync()
if articles.count > 0 {
tableView.scrollRowToVisible(0)
@ -85,9 +79,11 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
defer {
updateUnreadCount()
}
if articles == oldValue {
return
}
if articles.representSameArticlesInSameOrder(as: oldValue) {
// When the array is the same  same articles, same order
// but some data in some of the articles may have changed.
@ -96,7 +92,20 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
reloadVisibleCells()
return
}
updateShowIcons()
if let representedObjects = representedObjects, representedObjects.count == 1 && representedObjects.first is WebFeed {
showFeedNames = {
for article in articles {
if article.authors?.contains(where: { $0.name != nil }) ?? false {
return .byline
}
}
return .none
}()
} else {
showFeedNames = .feed
}
articleRowMap = [String: Int]()
tableView.reloadData()
}
@ -117,7 +126,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
private var articleRowMap = [String: Int]() // articleID: rowIndex
private var cellAppearance: TimelineCellAppearance!
private var cellAppearanceWithIcon: TimelineCellAppearance!
private var showFeedNames = false {
private var showFeedNames: TimelineShowFeedName = .none {
didSet {
if showFeedNames != oldValue {
updateShowIcons()
@ -663,7 +672,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, dateArrived: Date())
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, webFeedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, datePublished: nil, dateModified: nil, authors: nil, status: status)
let prototypeCellData = TimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil)
let prototypeCellData = TimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Prototype Feed Name", byline: nil, iconImage: nil, showIcon: false, featuredImage: nil)
let height = TimelineCellLayout.height(for: 100, cellData: prototypeCellData, appearance: cellAppearance)
return height
}
@ -810,7 +819,7 @@ extension TimelineViewController: NSTableViewDelegate {
private func configureTimelineCell(_ cell: TimelineTableCellView, article: Article) {
cell.objectValue = article
let iconImage = article.iconImage()
cell.cellData = TimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, iconImage: iconImage, showIcon: showIcons, featuredImage: nil)
cell.cellData = TimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, byline: article.byline(), iconImage: iconImage, showIcon: showIcons, featuredImage: nil)
}
private func iconFor(_ article: Article) -> IconImage? {
@ -946,20 +955,20 @@ private extension TimelineViewController {
}
func updateShowIcons() {
if showFeedNames {
if showFeedNames != .none {
self.showIcons = true
return
}
for article in articles {
if let authors = article.authors {
for author in authors {
if author.avatarURL != nil {
self.showIcons = true
return
for author in authors {
if author.avatarURL != nil {
self.showIcons = true
return
}
}
}
}
}
self.showIcons = false

View File

@ -55,7 +55,7 @@ struct MasterTimelineAccessibilityCellLayout: MasterTimelineCellLayout {
currentPoint.y = [self.titleRect, self.summaryRect].maxY()
if cellData.showFeedName {
if cellData.showFeedName != .none {
self.feedNameRect = MasterTimelineAccessibilityCellLayout.rectForFeedName(cellData, currentPoint, textAreaWidth)
currentPoint.y = self.feedNameRect.maxY
} else {

View File

@ -15,7 +15,8 @@ struct MasterTimelineCellData {
let summary: String
let dateString: String
let feedName: String
let showFeedName: Bool
let byline: String
let showFeedName: ShowFeedName
let iconImage: IconImage? // feed icon, user avatar, or favicon
let showIcon: Bool // Make space even when icon is nil
let featuredImage: UIImage? // image from within the article
@ -24,7 +25,7 @@ struct MasterTimelineCellData {
let numberOfLines: Int
let iconSize: IconSize
init(article: Article, showFeedName: Bool, feedName: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int, iconSize: IconSize) {
init(article: Article, showFeedName: ShowFeedName, feedName: String?, byline: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int, iconSize: IconSize) {
self.title = ArticleStringFormatter.truncatedTitle(article)
self.summary = ArticleStringFormatter.truncatedSummary(article)
@ -37,6 +38,12 @@ struct MasterTimelineCellData {
else {
self.feedName = ""
}
if let byline = byline {
self.byline = byline
} else {
self.byline = ""
}
self.showFeedName = showFeedName
@ -56,7 +63,8 @@ struct MasterTimelineCellData {
self.summary = ""
self.dateString = ""
self.feedName = ""
self.showFeedName = false
self.byline = ""
self.showFeedName = .none
self.showIcon = false
self.iconImage = nil
self.featuredImage = nil

View File

@ -172,12 +172,18 @@ private extension MasterTimelineTableViewCell {
}
func updateFeedNameView() {
if cellData.showFeedName {
switch cellData.showFeedName {
case .feed:
showView(feedNameView)
feedNameView.font = MasterTimelineDefaultCellLayout.feedNameFont
feedNameView.textColor = secondaryLabelColor
updateTextFieldText(feedNameView, cellData.feedName)
} else {
case .byline:
showView(feedNameView)
feedNameView.font = MasterTimelineDefaultCellLayout.feedNameFont
feedNameView.textColor = secondaryLabelColor
updateTextFieldText(feedNameView, cellData.byline)
case .none:
hideView(feedNameView)
}
}

View File

@ -489,7 +489,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, dateArrived: Date())
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, webFeedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, datePublished: nil, dateModified: nil, authors: nil, status: status)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Prototype Feed Name", byline: nil, iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize)
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
@ -649,7 +649,7 @@ private extension MasterTimelineViewController {
let showFeedNames = coordinator.showFeedNames
let showIcon = coordinator.showIcons && iconImage != nil
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines, iconSize: iconSize)
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, byline: article.byline(), iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines, iconSize: iconSize)
}

View File

@ -23,6 +23,12 @@ enum SearchScope: Int {
case global = 1
}
enum ShowFeedName {
case none
case byline
case feed
}
class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
var undoableCommands = [UndoableCommand]()
@ -159,7 +165,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
var timelineMiddleIndexPath: IndexPath?
private(set) var showFeedNames = false
private(set) var showFeedNames = ShowFeedName.none
private(set) var showIcons = false
var prevFeedIndexPath: IndexPath? {
@ -1454,12 +1460,19 @@ private extension SceneCoordinator {
func updateShowNamesAndIcons() {
if timelineFeed is WebFeed {
showFeedNames = false
showFeedNames = {
for article in articles {
if article.authors?.contains(where: { $0.name != nil }) ?? false {
return .byline
}
}
return .none
}()
} else {
showFeedNames = true
showFeedNames = .feed
}
if showFeedNames {
if showFeedNames == .feed {
self.showIcons = true
return
}

View File

@ -71,7 +71,7 @@ private extension TimelinePreviewTableViewController {
let iconImage = IconImage(AppAssets.faviconTemplateImage.withTintColor(AppAssets.secondaryAccentColor))
return MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Feed Name", iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.timelineNumberOfLines, iconSize: AppDefaults.timelineIconSize)
return MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.timelineNumberOfLines, iconSize: AppDefaults.timelineIconSize)
}
}