This commit is contained in:
Brent Simmons
2022-04-04 22:28:15 -07:00
10 changed files with 105 additions and 53 deletions

View File

@@ -34,7 +34,7 @@ final class FeedlyLogoutOperation: FeedlyOperation {
assert(Thread.isMainThread)
switch result {
case .success:
os_log("Logged out of %{public}@ account.", "\(account.type)")
os_log("Logged out of %{public}@ account.", log: log, "\(account.type)")
do {
try account.removeCredentials(type: .oauthAccessToken)
try account.removeCredentials(type: .oauthRefreshToken)
@@ -44,7 +44,7 @@ final class FeedlyLogoutOperation: FeedlyOperation {
didFinish()
case .failure(let error):
os_log("Logout failed because %{public}@.", error as NSError)
os_log("Logout failed because %{public}@.", log: log, error as NSError)
didFinish(with: error)
}
}

View File

@@ -37,6 +37,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
static let mainWindow = "mainWindow"
}
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
var userNotificationManager: UserNotificationManager!
var faviconDownloader: FaviconDownloader!
var imageDownloader: ImageDownloader!
@@ -199,7 +201,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
AppDefaults.shared.registerDefaults()
let isFirstRun = AppDefaults.shared.isFirstRun
if isFirstRun {
os_log(.debug, "Is first run.")
os_log(.debug, log: log, "Is first run.")
}
let localAccount = AccountManager.shared.defaultAccount
@@ -1010,12 +1012,12 @@ private extension AppDelegate {
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, "No account found from notification.")
os_log(.debug, log: log, "No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, "No article found from search using %@", articleID)
os_log(.debug, log: log, "No article found from search using %@", articleID)
return
}
account!.markArticles(article!, statusKey: .read, flag: true) { _ in }
@@ -1029,12 +1031,12 @@ private extension AppDelegate {
}
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, "No account found from notification.")
os_log(.debug, log: log, "No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, "No article found from search using %@", articleID)
os_log(.debug, log: log, "No article found from search using %@", articleID)
return
}
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }

View File

@@ -940,7 +940,7 @@ extension TimelineViewController: NSTableViewDelegate {
return [action]
@unknown default:
os_log(.error, "Unknown table row edge: %ld", edge.rawValue)
print("Unknown table row edge: \(edge.rawValue)")
}
return []

View File

@@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/microsoft/plcrashreporter.git",
"state": {
"branch": null,
"revision": "d747ab5de269cd44022bbe96ff9609d8626694ab",
"version": "1.9.0"
"revision": "6b27393cad517c067dceea85fadf050e70c4ceaa",
"version": "1.10.1"
}
},
{
@@ -105,8 +105,8 @@
"repositoryURL": "https://github.com/Ranchero-Software/Sparkle-Binary.git",
"state": {
"branch": null,
"revision": "67cd26321bdf4e77954cf6de7d9e6a20544f2030",
"version": "2.0.0"
"revision": "d1a8b3c98d96c601453f2e4230f1dd65b60d0581",
"version": "2.0.1"
}
},
{

View File

@@ -58,6 +58,10 @@ struct AppAssets {
static var appBadgeImage: UIImage = {
return UIImage(systemName: "app.badge")!
}()
static var articleAppearanceImage: UIImage = {
return UIImage(systemName: "textformat.size")!
}()
static var articleExtractorError: UIImage = {
return UIImage(named: "articleExtractorError")!

View File

@@ -408,11 +408,11 @@ private extension AppDelegate {
// set expiration handler
task.expirationHandler = { [weak task] in
DispatchQueue.main.sync {
self.suspendApplication()
}
os_log("Accounts refresh processing terminated for running too long.", log: self.log, type: .info)
task?.setTaskCompleted(success: false)
DispatchQueue.main.async {
self.suspendApplication()
task?.setTaskCompleted(success: false)
}
}
}
@@ -431,12 +431,12 @@ private extension AppDelegate {
resumeDatabaseProcessingIfNecessary()
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, "No account found from notification.")
os_log(.debug, log: self.log, "No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, "No article found from search using %@", articleID)
os_log(.debug, log: self.log, "No article found from search using %@", articleID)
return
}
account!.markArticles(article!, statusKey: .read, flag: true) { _ in }
@@ -459,12 +459,12 @@ private extension AppDelegate {
resumeDatabaseProcessingIfNecessary()
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, "No account found from notification.")
os_log(.debug, log: self.log, "No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, "No article found from search using %@", articleID)
os_log(.debug, log: self.log, "No article found from search using %@", articleID)
return
}
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }

View File

@@ -162,7 +162,6 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
searchBar.delegate = self
view.bringSubviewToFront(searchBar)
configureAppearanceMenu()
updateUI()
}
@@ -234,12 +233,20 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
starBarButtonItem.accLabelText = NSLocalizedString("Star Article", comment: "Star Article")
}
configureAppearanceMenu()
configureArticleExtractorMenu()
}
override func contentScrollView(for edge: NSDirectionalRectEdge) -> UIScrollView? {
return currentWebViewController?.webView?.scrollView
}
/// The appearance menu is different on iPhone and iPad.
/// On iPad, it's only the theme selector. On iPhone, the appearance menu
/// contains the the theme selector and full screen options.
/// - Parameter sender: `Any?`
@objc
func configureAppearanceMenu(_ sender: Any? = nil) {
@@ -266,6 +273,12 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
let themeMenu = UIMenu(title: "Theme", image: AppAssets.themeImage, identifier: nil, options: .singleSelection, children: [ defaultThemeMenu, customThemeMenu])
if UIDevice.current.userInterfaceIdiom == .pad {
appearanceBarButtonItem.image = AppAssets.themeImage
appearanceBarButtonItem.menu = themeMenu
return
}
var appearanceChildren: [UIMenuElement] = [themeMenu]
if let currentWebViewController = currentWebViewController {
@@ -291,8 +304,15 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
}
}
var feedManagementChildren = [UIMenuElement]()
let appearanceMenu = UIMenu(title: NSLocalizedString("Article Appearance", comment: "Appearance"), image: nil, identifier: nil, options: .displayInline, children: appearanceChildren)
let menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: [appearanceMenu])
appearanceBarButtonItem.image = AppAssets.articleAppearanceImage
appearanceBarButtonItem.menu = menu
}
private func configureArticleExtractorMenu() {
if let feed = article?.webFeed {
let extractorOn = feed.isArticleExtractorAlwaysOn ?? false
let readerAction = UIAction(title: NSLocalizedString("Always Use Reader View", comment: "Always Use Reader View"),
@@ -303,37 +323,16 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
state: extractorOn ? .on : .off) { [weak self] _ in
if feed.isArticleExtractorAlwaysOn == nil {
feed.isArticleExtractorAlwaysOn = true
self?.currentWebViewController?.toggleArticleExtractor()
} else {
feed.isArticleExtractorAlwaysOn?.toggle()
}
self?.configureAppearanceMenu()
self?.configureArticleExtractorMenu()
}
feedManagementChildren.append(readerAction)
let notifyOn = feed.isNotifyAboutNewArticles ?? false
let notifyAction = UIAction(title: NSLocalizedString("Notify About New Articles", comment: "Notify About New Articles"),
image: AppAssets.appBadgeImage,
identifier: nil,
discoverabilityTitle: nil,
attributes: [],
state: notifyOn ? .on : .off) { [weak self] _ in
if feed.isNotifyAboutNewArticles == nil {
feed.isNotifyAboutNewArticles = true
} else {
feed.isNotifyAboutNewArticles?.toggle()
}
self?.configureAppearanceMenu()
}
feedManagementChildren.append(notifyAction)
let menu = UIMenu(title: feed.nameForDisplay, image: AppAssets.articleExtractorOffSF, identifier: nil, options: .displayInline, children: [readerAction])
articleExtractorButton.menu = menu
articleExtractorButton.showsMenuAsPrimaryAction = false
}
let appearanceMenu = UIMenu(title: NSLocalizedString("Article Appearance", comment: "Appearance"), image: nil, identifier: nil, options: .displayInline, children: appearanceChildren)
let feedMgmtMenu = UIMenu(title: NSLocalizedString("Feed Management", comment: "Feed Management"), image: nil , identifier: nil, options: .displayInline, children: feedManagementChildren)
let menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: [appearanceMenu, feedMgmtMenu])
appearanceBarButtonItem.menu = menu
}
@@ -416,6 +415,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
@IBAction func toggleArticleExtractor(_ sender: Any) {
currentWebViewController?.toggleArticleExtractor()
configureArticleExtractorMenu()
}
@IBAction func nextUnread(_ sender: Any) {

View File

@@ -563,6 +563,14 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
}
}
if let rowChanges = changes.rowChanges {
for rowChange in rowChanges {
if let reloads = rowChange.reloadIndexPaths, !reloads.isEmpty {
tableView.reloadRows(at: reloads, with: .none)
}
}
}
completion?()
}

View File

@@ -25,6 +25,7 @@ struct ShadowTableChanges {
var section: Int
var deletes: Set<Int>?
var inserts: Set<Int>?
var reloads: Set<Int>?
var moves: Set<ShadowTableChanges.Move>?
var isEmpty: Bool {
@@ -41,15 +42,21 @@ struct ShadowTableChanges {
return inserts.map { IndexPath(row: $0, section: section) }
}
var reloadIndexPaths: [IndexPath]? {
guard let reloads = reloads else { return nil }
return reloads.map { IndexPath(row: $0, section: section) }
}
var moveIndexPaths: [(IndexPath, IndexPath)]? {
guard let moves = moves else { return nil }
return moves.map { (IndexPath(row: $0.from, section: section), IndexPath(row: $0.to, section: section)) }
}
init(section: Int, deletes: Set<Int>?, inserts: Set<Int>?, moves: Set<Move>?) {
init(section: Int, deletes: Set<Int>?, inserts: Set<Int>?, reloads: Set<Int>?, moves: Set<Move>?) {
self.section = section
self.deletes = deletes
self.inserts = inserts
self.reloads = reloads
self.moves = moves
}

View File

@@ -73,8 +73,16 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
private var fetchSerialNumber = 0
private let fetchRequestQueue = FetchRequestQueue()
// Which Containers are expanded
private var expandedTable = Set<ContainerIdentifier>()
// Which Containers used to be expanded. Reset by rebuilding the Shadow Table.
private var lastExpandedTable = Set<ContainerIdentifier>()
// Which Feeds have the Read Articles Filter enabled
private var readFilterEnabledTable = [FeedIdentifier: Bool]()
// Flattened tree structure for the Sidebar
private var shadowTable = [(sectionID: String, feedNodes: [FeedNode])]()
private(set) var preSearchTimelineFeed: Feed?
@@ -675,8 +683,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
rebuildBackingStores()
}
/// This is a special function that expects the caller to change the disclosure arrow state outside this function.
/// Failure to do so will get the Sidebar into an invalid state.
func expand(_ node: Node) {
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
lastExpandedTable.insert(containerID)
expand(containerID)
}
@@ -698,8 +709,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
clearTimelineIfNoLongerAvailable()
}
/// This is a special function that expects the caller to change the disclosure arrow state outside this function.
/// Failure to do so will get the Sidebar into an invalid state.
func collapse(_ node: Node) {
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
lastExpandedTable.remove(containerID)
collapse(containerID)
}
@@ -1080,7 +1094,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
rebuildBackingStores(initialLoad: initialLoad, completion: {
self.treeControllerDelegate.resetFilterExceptions()
self.selectFeed(webFeed, animations: animations, completion: completion)
self.selectFeed(nil) {
self.selectFeed(webFeed, animations: animations, completion: completion)
}
})
}
@@ -1383,6 +1399,7 @@ private extension SceneCoordinator {
let toolbarAppearance = UIToolbarAppearance()
navController.toolbar.standardAppearance = toolbarAppearance
navController.toolbar.compactAppearance = toolbarAppearance
navController.toolbar.scrollEdgeAppearance = toolbarAppearance
navController.toolbar.tintColor = AppAssets.primaryAccentColor
}
@@ -1512,9 +1529,10 @@ private extension SceneCoordinator {
currentFeedIndexPath = indexPathFor(timelineFeed as AnyObject)
}
// Compute the differences in the shadow table rows
// Compute the differences in the shadow table rows and the expanded table entries
var changes = [ShadowTableChanges.RowChanges]()
let expandedTableDifference = lastExpandedTable.symmetricDifference(expandedTable)
for (section, newSectionRows) in newShadowTable.enumerated() {
var moves = Set<ShadowTableChanges.Move>()
var inserts = Set<Int>()
@@ -1540,9 +1558,22 @@ private extension SceneCoordinator {
}
}
changes.append(ShadowTableChanges.RowChanges(section: section, deletes: deletes, inserts: inserts, moves: moves))
// We need to reload the difference in expanded rows to get the disclosure arrows correct when programmatically changing their state
var reloads = Set<Int>()
for (index, newFeedNode) in newSectionRows.feedNodes.enumerated() {
if let newFeedNodeContainerID = (newFeedNode.node.representedObject as? Container)?.containerID {
if expandedTableDifference.contains(newFeedNodeContainerID) {
reloads.insert(index)
}
}
}
changes.append(ShadowTableChanges.RowChanges(section: section, deletes: deletes, inserts: inserts, reloads: reloads, moves: moves))
}
lastExpandedTable = expandedTable
// Compute the difference in the shadow table sections
var moves = Set<ShadowTableChanges.Move>()
var inserts = Set<Int>()