mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge branch 'extension-point'
This commit is contained in:
@@ -29,23 +29,32 @@ class AddFeedController: AddFeedWindowControllerDelegate {
|
||||
private var titleFromFeed: String?
|
||||
|
||||
init(hostWindow: NSWindow) {
|
||||
|
||||
self.hostWindow = hostWindow
|
||||
}
|
||||
|
||||
func showAddFeedSheet(_ urlString: String?, _ name: String?, _ account: Account?, _ folder: Folder?) {
|
||||
|
||||
func showAddFeedSheet(_ type: AddFeedWindowControllerType, _ urlString: String? = nil, _ name: String? = nil, _ account: Account? = nil, _ folder: Folder? = nil) {
|
||||
let folderTreeControllerDelegate = FolderTreeControllerDelegate()
|
||||
let folderTreeController = TreeController(delegate: folderTreeControllerDelegate)
|
||||
|
||||
addFeedWindowController = AddFeedWindowController(urlString: urlString ?? urlStringFromPasteboard, name: name, account: account, folder: folder, folderTreeController: folderTreeController, delegate: self)
|
||||
switch type {
|
||||
case .webFeed:
|
||||
addFeedWindowController = AddWebFeedWindowController(urlString: urlString ?? urlStringFromPasteboard,
|
||||
name: name,
|
||||
account: account,
|
||||
folder: folder,
|
||||
folderTreeController: folderTreeController,
|
||||
delegate: self)
|
||||
case .twitterFeed:
|
||||
addFeedWindowController = AddTwitterFeedWindowController(folderTreeController: folderTreeController,
|
||||
delegate: self)
|
||||
}
|
||||
|
||||
addFeedWindowController!.runSheetOnWindow(hostWindow)
|
||||
}
|
||||
|
||||
// MARK: AddFeedWindowControllerDelegate
|
||||
|
||||
func addFeedWindowController(_: AddFeedWindowController, userEnteredURL url: URL, userEnteredTitle title: String?, container: Container) {
|
||||
|
||||
closeAddFeedSheet(NSApplication.ModalResponse.OK)
|
||||
|
||||
guard let accountAndFolderSpecifier = accountAndFolderFromContainer(container) else {
|
||||
@@ -81,11 +90,9 @@ class AddFeedController: AddFeedWindowControllerDelegate {
|
||||
}
|
||||
|
||||
beginShowingProgress()
|
||||
|
||||
}
|
||||
|
||||
func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController) {
|
||||
|
||||
closeAddFeedSheet(NSApplication.ModalResponse.cancel)
|
||||
}
|
||||
|
||||
@@ -106,7 +113,6 @@ private extension AddFeedController {
|
||||
}
|
||||
|
||||
func accountAndFolderFromContainer(_ container: Container) -> AccountAndFolderSpecifier? {
|
||||
|
||||
if let account = container as? Account {
|
||||
return AccountAndFolderSpecifier(account: account, folder: nil)
|
||||
}
|
||||
@@ -117,7 +123,6 @@ private extension AddFeedController {
|
||||
}
|
||||
|
||||
func closeAddFeedSheet(_ returnCode: NSApplication.ModalResponse) {
|
||||
|
||||
if let sheetWindow = addFeedWindowController?.window {
|
||||
hostWindow.endSheet(sheetWindow, returnCode: returnCode)
|
||||
}
|
||||
@@ -126,17 +131,14 @@ private extension AddFeedController {
|
||||
// MARK: Errors
|
||||
|
||||
func showAlreadySubscribedError(_ urlString: String) {
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = NSLocalizedString("Already subscribed", comment: "Feed finder")
|
||||
alert.informativeText = NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder")
|
||||
|
||||
alert.beginSheetModal(for: hostWindow)
|
||||
}
|
||||
|
||||
func showInitialDownloadError(_ error: Error) {
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = NSLocalizedString("Download Error", comment: "Feed finder")
|
||||
@@ -144,31 +146,27 @@ private extension AddFeedController {
|
||||
let formatString = NSLocalizedString("Can’t add this feed because of a download error: “%@”", comment: "Feed finder")
|
||||
let errorText = NSString.localizedStringWithFormat(formatString as NSString, error.localizedDescription)
|
||||
alert.informativeText = errorText as String
|
||||
|
||||
alert.beginSheetModal(for: hostWindow)
|
||||
}
|
||||
|
||||
func showNoFeedsErrorMessage() {
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = NSLocalizedString("Feed not found", comment: "Feed finder")
|
||||
alert.informativeText = NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder")
|
||||
|
||||
alert.beginSheetModal(for: hostWindow)
|
||||
}
|
||||
|
||||
// MARK: Progress
|
||||
|
||||
func beginShowingProgress() {
|
||||
|
||||
runIndeterminateProgressWithMessage(NSLocalizedString("Finding feed…", comment:"Feed finder"))
|
||||
}
|
||||
|
||||
func endShowingProgress() {
|
||||
|
||||
stopIndeterminateProgress()
|
||||
hostWindow.makeKeyAndOrderFront(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
30
Mac/MainWindow/AddFeed/AddFeedWIndowController.swift
Normal file
30
Mac/MainWindow/AddFeed/AddFeedWIndowController.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// AddFeedWIndowController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/21/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Account
|
||||
|
||||
enum AddFeedWindowControllerType {
|
||||
case webFeed
|
||||
case twitterFeed
|
||||
}
|
||||
|
||||
protocol AddFeedWindowControllerDelegate: class {
|
||||
|
||||
// userEnteredURL will have already been validated and normalized.
|
||||
func addFeedWindowController(_: AddFeedWindowController, userEnteredURL: URL, userEnteredTitle: String?, container: Container)
|
||||
func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController)
|
||||
|
||||
}
|
||||
|
||||
protocol AddFeedWindowController {
|
||||
|
||||
var window: NSWindow? { get }
|
||||
func runSheetOnWindow(_ hostWindow: NSWindow)
|
||||
|
||||
}
|
||||
198
Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift
Normal file
198
Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift
Normal file
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// AddTwitterFeedWindowController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/21/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import RSCore
|
||||
import RSTree
|
||||
import Articles
|
||||
import Account
|
||||
|
||||
class AddTwitterFeedWindowController : NSWindowController, AddFeedWindowController {
|
||||
|
||||
@IBOutlet weak var typePopupButton: NSPopUpButton!
|
||||
@IBOutlet weak var typeDescriptionLabel: NSTextField!
|
||||
|
||||
@IBOutlet weak var accountLabel: NSTextField!
|
||||
@IBOutlet weak var accountPopupButton: NSPopUpButton!
|
||||
@IBOutlet weak var screenSearchTextField: NSTextField!
|
||||
|
||||
@IBOutlet var nameTextField: NSTextField!
|
||||
@IBOutlet var addButton: NSButton!
|
||||
@IBOutlet var folderPopupButton: NSPopUpButton!
|
||||
|
||||
private var urlString: String?
|
||||
private var initialName: String?
|
||||
private weak var initialAccount: Account?
|
||||
private var initialFolder: Folder?
|
||||
private weak var delegate: AddFeedWindowControllerDelegate?
|
||||
private var folderTreeController: TreeController!
|
||||
|
||||
private var userEnteredScreenSearch: String? {
|
||||
var s = screenSearchTextField.stringValue
|
||||
s = s.collapsingWhitespace
|
||||
if s.isEmpty {
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
private var userEnteredTitle: String? {
|
||||
var s = nameTextField.stringValue
|
||||
s = s.collapsingWhitespace
|
||||
if s.isEmpty {
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var hostWindow: NSWindow!
|
||||
|
||||
convenience init(folderTreeController: TreeController, delegate: AddFeedWindowControllerDelegate?) {
|
||||
self.init(windowNibName: NSNib.Name("AddTwitterFeedSheet"))
|
||||
self.folderTreeController = folderTreeController
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
func runSheetOnWindow(_ hostWindow: NSWindow) {
|
||||
hostWindow.beginSheet(window!) { (returnCode: NSApplication.ModalResponse) -> Void in
|
||||
}
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
|
||||
let accountMenu = NSMenu()
|
||||
for feedProvider in ExtensionPointManager.shared.activeFeedProviders {
|
||||
if let twitterFeedProvider = feedProvider as? TwitterFeedProvider {
|
||||
let accountMenuItem = NSMenuItem()
|
||||
accountMenuItem.title = "@\(twitterFeedProvider.screenName)"
|
||||
accountMenu.addItem(accountMenuItem)
|
||||
}
|
||||
}
|
||||
accountPopupButton.menu = accountMenu
|
||||
|
||||
folderPopupButton.menu = FolderTreeMenu.createFolderPopupMenu(with: folderTreeController.rootNode)
|
||||
|
||||
if let container = AddWebFeedDefaultContainer.defaultContainer {
|
||||
if let folder = container as? Folder, let account = folder.account {
|
||||
FolderTreeMenu.select(account: account, folder: folder, in: folderPopupButton)
|
||||
} else {
|
||||
if let account = container as? Account {
|
||||
FolderTreeMenu.select(account: account, folder: nil, in: folderPopupButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateUI()
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func selectedType(_ sender: Any) {
|
||||
screenSearchTextField.stringValue = ""
|
||||
updateUI()
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any?) {
|
||||
cancelSheet()
|
||||
}
|
||||
|
||||
@IBAction func addFeed(_ sender: Any?) {
|
||||
guard let type = TwitterFeedType(rawValue: typePopupButton.selectedItem?.tag ?? 0),
|
||||
let atUsername = accountPopupButton.selectedItem?.title else { return }
|
||||
|
||||
let username = String(atUsername[atUsername.index(atUsername.startIndex, offsetBy: 1)..<atUsername.endIndex])
|
||||
|
||||
var screenSearch = userEnteredScreenSearch
|
||||
if let screenName = screenSearch, type == .screenName && screenName.starts(with: "@") {
|
||||
screenSearch = String(screenName[screenName.index(screenName.startIndex, offsetBy: 1)..<screenName.endIndex])
|
||||
}
|
||||
|
||||
guard let url = TwitterFeedProvider.buildURL(type, username: username, screenName: screenSearch, searchField: screenSearch) else { return }
|
||||
|
||||
let container = selectedContainer()!
|
||||
AddWebFeedDefaultContainer.saveDefaultContainer(container)
|
||||
delegate?.addFeedWindowController(self, userEnteredURL: url, userEnteredTitle: userEnteredTitle, container: container)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AddTwitterFeedWindowController: NSTextFieldDelegate {
|
||||
|
||||
func controlTextDidChange(_ obj: Notification) {
|
||||
updateUI()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension AddTwitterFeedWindowController {
|
||||
|
||||
private func updateUI() {
|
||||
|
||||
switch typePopupButton.selectedItem?.tag ?? 0 {
|
||||
case 0:
|
||||
|
||||
accountLabel.isHidden = false
|
||||
accountPopupButton.isHidden = false
|
||||
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets from everyone you follow", comment: "Home Timeline")
|
||||
screenSearchTextField.isHidden = true
|
||||
addButton.isEnabled = true
|
||||
|
||||
case 1:
|
||||
|
||||
accountLabel.isHidden = false
|
||||
accountPopupButton.isHidden = false
|
||||
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets mentioning you", comment: "Mentions")
|
||||
screenSearchTextField.isHidden = true
|
||||
addButton.isEnabled = true
|
||||
|
||||
case 2:
|
||||
|
||||
accountLabel.isHidden = true
|
||||
accountPopupButton.isHidden = true
|
||||
|
||||
var screenSearch = userEnteredScreenSearch
|
||||
if screenSearch != nil {
|
||||
if let screenName = screenSearch, screenName.starts(with: "@") {
|
||||
screenSearch = String(screenName[screenName.index(screenName.startIndex, offsetBy: 1)..<screenName.endIndex])
|
||||
}
|
||||
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets from @\(screenSearch!)", comment: "Home Timeline")
|
||||
} else {
|
||||
typeDescriptionLabel.stringValue = ""
|
||||
}
|
||||
|
||||
screenSearchTextField.placeholderString = NSLocalizedString("@name", comment: "@name")
|
||||
screenSearchTextField.isHidden = false
|
||||
addButton.isEnabled = !screenSearchTextField.stringValue.isEmpty
|
||||
|
||||
default:
|
||||
|
||||
accountLabel.isHidden = true
|
||||
accountPopupButton.isHidden = true
|
||||
|
||||
if !screenSearchTextField.stringValue.isEmpty {
|
||||
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets that contain \(screenSearchTextField.stringValue)", comment: "Home Timeline")
|
||||
} else {
|
||||
typeDescriptionLabel.stringValue = ""
|
||||
}
|
||||
|
||||
screenSearchTextField.placeholderString = nil
|
||||
screenSearchTextField.isHidden = false
|
||||
addButton.isEnabled = !screenSearchTextField.stringValue.isEmpty
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func cancelSheet() {
|
||||
delegate?.addFeedWindowControllerUserDidCancel(self)
|
||||
}
|
||||
|
||||
func selectedContainer() -> Container? {
|
||||
return folderPopupButton.selectedItem?.representedObject as? Container
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,7 @@ import RSTree
|
||||
import Articles
|
||||
import Account
|
||||
|
||||
protocol AddFeedWindowControllerDelegate: class {
|
||||
|
||||
// userEnteredURL will have already been validated and normalized.
|
||||
func addFeedWindowController(_: AddFeedWindowController, userEnteredURL: URL, userEnteredTitle: String?, container: Container)
|
||||
|
||||
func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController)
|
||||
}
|
||||
|
||||
class AddFeedWindowController : NSWindowController {
|
||||
class AddWebFeedWindowController : NSWindowController, AddFeedWindowController {
|
||||
|
||||
@IBOutlet var urlTextField: NSTextField!
|
||||
@IBOutlet var nameTextField: NSTextField!
|
||||
@@ -46,7 +38,7 @@ class AddFeedWindowController : NSWindowController {
|
||||
var hostWindow: NSWindow!
|
||||
|
||||
convenience init(urlString: String?, name: String?, account: Account?, folder: Folder?, folderTreeController: TreeController, delegate: AddFeedWindowControllerDelegate?) {
|
||||
self.init(windowNibName: NSNib.Name("AddFeedSheet"))
|
||||
self.init(windowNibName: NSNib.Name("AddWebFeedSheet"))
|
||||
self.urlString = urlString
|
||||
self.initialName = name
|
||||
self.initialAccount = account
|
||||
@@ -127,7 +119,7 @@ class AddFeedWindowController : NSWindowController {
|
||||
}
|
||||
}
|
||||
|
||||
private extension AddFeedWindowController {
|
||||
private extension AddWebFeedWindowController {
|
||||
|
||||
private func updateUI() {
|
||||
addButton.isEnabled = urlTextField.stringValue.mayBeURL
|
||||
@@ -1,13 +1,15 @@
|
||||
// Add the mouse listeners for the above functions
|
||||
function linkHover() {
|
||||
window.onmouseover = function(event) {
|
||||
if (event.target.matches('a')) {
|
||||
window.webkit.messageHandlers.mouseDidEnter.postMessage(event.target.href);
|
||||
var closestAnchor = event.target.closest('a')
|
||||
if (closestAnchor) {
|
||||
window.webkit.messageHandlers.mouseDidEnter.postMessage(closestAnchor.href);
|
||||
}
|
||||
}
|
||||
window.onmouseout = function(event) {
|
||||
if (event.target.matches('a')) {
|
||||
window.webkit.messageHandlers.mouseDidExit.postMessage(event.target.href);
|
||||
var closestAnchor = event.target.closest('a')
|
||||
if (closestAnchor) {
|
||||
window.webkit.messageHandlers.mouseDidExit.postMessage(closestAnchor.href);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,25 +18,20 @@ import RSCore
|
||||
}
|
||||
|
||||
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] {
|
||||
|
||||
return proposedServices + SharingServicePickerDelegate.customSharingServices(for: items)
|
||||
|
||||
}
|
||||
|
||||
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, delegateFor sharingService: NSSharingService) -> NSSharingServiceDelegate? {
|
||||
return sharingServiceDelegate
|
||||
}
|
||||
|
||||
private static let sendToCommands: [SendToCommand] = {
|
||||
return [SendToMicroBlogCommand(), SendToMarsEditCommand()]
|
||||
}()
|
||||
|
||||
static func customSharingServices(for items: [Any]) -> [NSSharingService] {
|
||||
let customServices = sendToCommands.compactMap { (sendToCommand) -> NSSharingService? in
|
||||
let customServices = ExtensionPointManager.shared.activeSendToCommands.compactMap { (sendToCommand) -> NSSharingService? in
|
||||
|
||||
guard let object = items.first else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard sendToCommand.canSendObject(object, selectedText: nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -570,11 +570,11 @@ private extension SidebarOutlineDataSource {
|
||||
|
||||
// Show the add-feed sheet.
|
||||
if let account = parentNode.representedObject as? Account {
|
||||
appDelegate.addFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: nil)
|
||||
appDelegate.addWebFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: nil)
|
||||
} else {
|
||||
let account = parentNode.parent?.representedObject as? Account
|
||||
let folder = parentNode.representedObject as? Folder
|
||||
appDelegate.addFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: folder)
|
||||
appDelegate.addWebFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: folder)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -129,7 +129,7 @@ private extension SidebarViewController {
|
||||
|
||||
let menu = NSMenu(title: "")
|
||||
|
||||
menu.addItem(withTitle: NSLocalizedString("New Feed", comment: "Command"), action: #selector(AppDelegate.showAddFeedWindow(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: NSLocalizedString("New Feed", comment: "Command"), action: #selector(AppDelegate.showAddWebFeedWindow(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: NSLocalizedString("New Folder", comment: "Command"), action: #selector(AppDelegate.showAddFolderWindow(_:)), keyEquivalent: "")
|
||||
|
||||
return menu
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.byline().isEmpty {
|
||||
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,25 @@ private extension TimelineViewController {
|
||||
}
|
||||
|
||||
func updateShowIcons() {
|
||||
if showFeedNames {
|
||||
if showFeedNames == .feed {
|
||||
self.showIcons = true
|
||||
return
|
||||
}
|
||||
|
||||
if showFeedNames == .none {
|
||||
self.showIcons = false
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user