mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Bring in code from previous iOS release.
This commit is contained in:
@@ -216,7 +216,6 @@
|
||||
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
|
||||
519ED456244828C3007F8E94 /* AddExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */; };
|
||||
519ED47A24482AEB007F8E94 /* EnableExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */; };
|
||||
519ED47C24488C6F007F8E94 /* ExtensionInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */; };
|
||||
51A052CE244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; };
|
||||
51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */; };
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; };
|
||||
@@ -911,7 +910,6 @@
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExtensionPointViewController.swift; sourceTree = "<group>"; };
|
||||
519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointViewController.swift; sourceTree = "<group>"; };
|
||||
519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionInspectorViewController.swift; sourceTree = "<group>"; };
|
||||
51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedWIndowController.swift; path = AddFeed/AddFeedWIndowController.swift; sourceTree = "<group>"; };
|
||||
51A1698F235E10D600EB091F /* LocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
||||
@@ -1425,7 +1423,6 @@
|
||||
children = (
|
||||
516A09412361248000EAE89B /* Inspector.storyboard */,
|
||||
51A16991235E10D600EB091F /* AccountInspectorViewController.swift */,
|
||||
519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */,
|
||||
5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */,
|
||||
5141E7382373C18B0013FF27 /* WebFeedInspectorViewController.swift */,
|
||||
);
|
||||
@@ -3199,7 +3196,6 @@
|
||||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
|
||||
5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */,
|
||||
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */,
|
||||
519ED47C24488C6F007F8E94 /* ExtensionInspectorViewController.swift in Sources */,
|
||||
51C4CFF224D37D1F00AF9874 /* Secrets.swift in Sources */,
|
||||
51C452A022650A1900C03939 /* WebFeedIconDownloader.swift in Sources */,
|
||||
51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Account
|
||||
|
||||
enum CloudKitAccountViewControllerError: LocalizedError {
|
||||
@@ -30,7 +31,7 @@ class CloudKitAccountViewController: UITableViewController {
|
||||
}
|
||||
|
||||
private func setupFooter() {
|
||||
footerLabel.text = NSLocalizedString("Feeds in your iCloud account will be synced across your Mac and iOS devices.\n\nImportant note: while NetNewsWire itself is very fast, iCloud syncing is sometimes very slow. This can happen after adding a number of feeds and when setting it up on a new device.\n\nIf that happens to you, it may appear stuck. But don’t worry — it’s not. Just let it run.", comment: "iCloud")
|
||||
footerLabel.text = NSLocalizedString("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud")
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
@@ -62,5 +63,10 @@ class CloudKitAccountViewController: UITableViewController {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
|
||||
let vc = SFSafariViewController(url: CloudKitWebDocumentation.limitationsAndSolutionsURL)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,6 @@ import RSCore
|
||||
import RSTree
|
||||
import RSParser
|
||||
|
||||
enum AddFeedType {
|
||||
case web
|
||||
}
|
||||
|
||||
class AddFeedViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@@ -37,12 +33,7 @@ class AddFeedViewController: UITableViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
switch addFeedType {
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
activityIndicator.isHidden = true
|
||||
activityIndicator.color = .label
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
|
||||
|
||||
final class AppDefaults {
|
||||
|
||||
static let defaultThemeName = "Defaults"
|
||||
static let defaultThemeName = "Default"
|
||||
|
||||
static let shared = AppDefaults()
|
||||
private init() {}
|
||||
@@ -41,7 +41,6 @@ final class AppDefaults {
|
||||
|
||||
struct Key {
|
||||
static let userInterfaceColorPalette = "userInterfaceColorPalette"
|
||||
static let activeExtensionPointIDs = "activeExtensionPointIDs"
|
||||
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
|
||||
static let firstRunDate = "firstRunDate"
|
||||
static let timelineGroupByFeed = "timelineGroupByFeed"
|
||||
@@ -114,15 +113,6 @@ final class AppDefaults {
|
||||
}
|
||||
}
|
||||
|
||||
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
|
||||
get {
|
||||
return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs)
|
||||
}
|
||||
}
|
||||
|
||||
var useSystemBrowser: Bool {
|
||||
get {
|
||||
return UserDefaults.standard.bool(forKey: Key.useSystemBrowser)
|
||||
|
||||
@@ -45,7 +45,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||
var webFeedIconDownloader: WebFeedIconDownloader!
|
||||
var extensionContainersFile: ExtensionContainersFile!
|
||||
var extensionFeedAddRequestFile: ExtensionFeedAddRequestFile!
|
||||
|
||||
var widgetDataEncoder: WidgetDataEncoder!
|
||||
|
||||
var unreadCount = 0 {
|
||||
didSet {
|
||||
if unreadCount != oldValue {
|
||||
@@ -72,8 +73,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||
let documentThemesFolderPath = String(documentThemesFolder.suffix(from: documentAccountsFolder.index(documentThemesFolder.startIndex, offsetBy: 7)))
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: documentThemesFolderPath)
|
||||
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountRefreshDidFinish(_:)), name: .AccountRefreshDidFinish, object: nil)
|
||||
}
|
||||
@@ -114,8 +113,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||
extensionContainersFile = ExtensionContainersFile()
|
||||
extensionFeedAddRequestFile = ExtensionFeedAddRequestFile()
|
||||
|
||||
widgetDataEncoder = WidgetDataEncoder()
|
||||
|
||||
syncTimer = ArticleStatusSyncTimer()
|
||||
|
||||
|
||||
#if DEBUG
|
||||
syncTimer!.update()
|
||||
#endif
|
||||
@@ -175,9 +176,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
||||
syncTimer?.invalidate()
|
||||
scheduleBackgroundFeedRefresh()
|
||||
syncArticleStatus()
|
||||
widgetDataEncoder.encode()
|
||||
waitForSyncTasksToFinish()
|
||||
}
|
||||
|
||||
|
||||
func prepareAccountsForForeground() {
|
||||
extensionFeedAddRequestFile.resume()
|
||||
syncTimer?.update()
|
||||
@@ -293,7 +295,7 @@ private extension AppDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
if AccountManager.shared.refreshInProgress || isSyncArticleStatusRunning {
|
||||
if AccountManager.shared.refreshInProgress || isSyncArticleStatusRunning || widgetDataEncoder.isRunning {
|
||||
os_log("Waiting for sync to finish...", log: self.log, type: .info)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
||||
self?.waitToComplete(completion: completion)
|
||||
@@ -387,9 +389,9 @@ private extension AppDelegate {
|
||||
/// - Parameter task: `BGAppRefreshTask`
|
||||
/// - Warning: As of Xcode 11 beta 2, when triggered from the debugger this doesn't work.
|
||||
func performBackgroundFeedRefresh(with task: BGAppRefreshTask) {
|
||||
|
||||
|
||||
scheduleBackgroundFeedRefresh() // schedule next refresh
|
||||
|
||||
|
||||
os_log("Woken to perform account refresh.", log: self.log, type: .info)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
@@ -398,26 +400,22 @@ private extension AppDelegate {
|
||||
}
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) { [unowned self] in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
if #available(iOS 14, *) {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
}
|
||||
self.suspendApplication()
|
||||
os_log("Account refresh operation completed.", log: self.log, type: .info)
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle Notification Actions
|
||||
@@ -445,9 +443,6 @@ private extension AppDelegate {
|
||||
self.prepareAccountsForBackground()
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
if #available(iOS 14, *) {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
}
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
@@ -474,9 +469,6 @@ private extension AppDelegate {
|
||||
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
if #available(iOS 14, *) {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
}
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Account
|
||||
|
||||
class AccountInspectorViewController: UITableViewController {
|
||||
@@ -16,7 +17,8 @@ class AccountInspectorViewController: UITableViewController {
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
@IBOutlet weak var activeSwitch: UISwitch!
|
||||
@IBOutlet weak var deleteAccountButton: VibrantButton!
|
||||
|
||||
@IBOutlet weak var limitationsAndSolutionsButton: UIButton!
|
||||
|
||||
var isModal = false
|
||||
weak var account: Account?
|
||||
|
||||
@@ -36,6 +38,10 @@ class AccountInspectorViewController: UITableViewController {
|
||||
deleteAccountButton.setTitle(NSLocalizedString("Remove Account", comment: "Remove Account"), for: .normal)
|
||||
}
|
||||
|
||||
if account.type != .cloudKit {
|
||||
limitationsAndSolutionsButton.isHidden = true
|
||||
}
|
||||
|
||||
if isModal {
|
||||
let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
|
||||
navigationItem.leftBarButtonItem = doneBarButtonItem
|
||||
@@ -115,6 +121,12 @@ class AccountInspectorViewController: UITableViewController {
|
||||
|
||||
present(alertController, animated: true)
|
||||
}
|
||||
|
||||
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
|
||||
let vc = SFSafariViewController(url: CloudKitWebDocumentation.limitationsAndSolutionsURL)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Table View
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// ExtensionPointInspectorViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/16/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ExtensionPointInspectorViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var extensionDescription: UILabel!
|
||||
var extensionPoint: ExtensionPoint?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
guard let extensionPoint = extensionPoint else { return }
|
||||
navigationItem.title = extensionPoint.title
|
||||
extensionDescription.attributedText = extensionPoint.description
|
||||
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||
}
|
||||
|
||||
@IBAction func disable(_ sender: Any) {
|
||||
guard let extensionPoint = extensionPoint else { return }
|
||||
|
||||
let title = NSLocalizedString("Deactivate Extension", comment: "Deactivate Extension")
|
||||
let extensionPointTypeTitle = extensionPoint.extensionPointID.extensionPointType.title
|
||||
let message = NSLocalizedString("Are you sure you want to deactivate the \(extensionPointTypeTitle) extension “\(extensionPoint.title)”?", comment: "Deactivate text")
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
let markTitle = NSLocalizedString("Deactivate", comment: "Deactivate")
|
||||
let markAction = UIAlertAction(title: markTitle, style: .default) { [weak self] (action) in
|
||||
ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint.extensionPointID)
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
alertController.addAction(markAction)
|
||||
alertController.preferredAction = markAction
|
||||
|
||||
present(alertController, animated: true)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Table View
|
||||
|
||||
extension ExtensionPointInspectorViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
guard let extensionPoint = extensionPoint else { return nil }
|
||||
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = extensionPoint.image
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
if indexPath.section > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,9 +47,12 @@ class KeyboardManager {
|
||||
|
||||
static func createKeyCommand(title: String, action: String, input: String, modifiers: UIKeyModifierFlags) -> UIKeyCommand {
|
||||
let selector = NSSelectorFromString(action)
|
||||
return UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on)
|
||||
let keyCommand = UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on)
|
||||
if #available(iOS 15.0, *) {
|
||||
keyCommand.wantsPriorityOverSystemBehavior = true
|
||||
}
|
||||
return keyCommand
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension KeyboardManager {
|
||||
@@ -58,14 +61,18 @@ private extension KeyboardManager {
|
||||
guard let input = createKeyCommandInput(keyEntry: keyEntry) else { return nil }
|
||||
let modifiers = createKeyModifierFlags(keyEntry: keyEntry)
|
||||
let action = keyEntry["action"] as! String
|
||||
|
||||
|
||||
if let title = keyEntry["title"] as? String {
|
||||
return KeyboardManager.createKeyCommand(title: title, action: action, input: input, modifiers: modifiers)
|
||||
} else {
|
||||
return UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action))
|
||||
let keyCommand = UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action))
|
||||
if #available(iOS 15.0, *) {
|
||||
keyCommand.wantsPriorityOverSystemBehavior = true
|
||||
}
|
||||
return keyCommand
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func createKeyCommandInput(keyEntry: [String: Any]) -> String? {
|
||||
guard let key = keyEntry["key"] as? String else { return nil }
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedSettingDidChange(_:)), name: .WebFeedSettingDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(configureContextMenu(_:)), name: .ActiveExtensionPointsDidChange, object: nil)
|
||||
|
||||
refreshControl = UIRefreshControl()
|
||||
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||
@@ -504,6 +503,13 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
}
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
if action == #selector(UIResponder.delete(_:)) {
|
||||
return isFirstResponder
|
||||
}
|
||||
return super.canPerformAction(action, withSender: sender)
|
||||
}
|
||||
|
||||
@objc func expandSelectedRows(_ sender: Any?) {
|
||||
if let indexPath = coordinator.currentFeedIndexPath, let node = coordinator.nodeFor(indexPath) {
|
||||
coordinator.expand(node)
|
||||
@@ -608,6 +614,14 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
}
|
||||
}
|
||||
|
||||
if let rowChanges = changes.rowChanges {
|
||||
for rowChange in rowChanges {
|
||||
if let reloads = rowChange.reloadIndexPaths, !reloads.isEmpty {
|
||||
tableView.reloadRows(at: reloads, with: .none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completion?()
|
||||
}
|
||||
|
||||
@@ -635,7 +649,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
|
||||
var menuItems: [UIAction] = []
|
||||
|
||||
let addWebFeedActionTitle = NSLocalizedString("Add Web Feed", comment: "Add Web Feed")
|
||||
let addWebFeedActionTitle = NSLocalizedString("Add Feed", comment: "Add Feed")
|
||||
let addWebFeedAction = UIAction(title: addWebFeedActionTitle, image: AppAssets.plus) { _ in
|
||||
self.coordinator.showAddWebFeed()
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
import Foundation
|
||||
|
||||
struct ShadowTableChanges {
|
||||
|
||||
|
||||
struct Move: Hashable {
|
||||
var from: Int
|
||||
var to: Int
|
||||
|
||||
|
||||
init(_ from: Int, _ to: Int) {
|
||||
self.from = from
|
||||
self.to = to
|
||||
@@ -21,38 +21,44 @@ struct ShadowTableChanges {
|
||||
}
|
||||
|
||||
struct RowChanges {
|
||||
|
||||
|
||||
var section: Int
|
||||
var deletes: Set<Int>?
|
||||
var inserts: Set<Int>?
|
||||
var reloads: Set<Int>?
|
||||
var moves: Set<ShadowTableChanges.Move>?
|
||||
|
||||
|
||||
var isEmpty: Bool {
|
||||
return (deletes?.isEmpty ?? true) && (inserts?.isEmpty ?? true) && (moves?.isEmpty ?? true)
|
||||
}
|
||||
|
||||
|
||||
var deleteIndexPaths: [IndexPath]? {
|
||||
guard let deletes = deletes else { return nil }
|
||||
return deletes.map { IndexPath(row: $0, section: section) }
|
||||
}
|
||||
|
||||
|
||||
var insertIndexPaths: [IndexPath]? {
|
||||
guard let inserts = inserts else { return nil }
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var deletes: Set<Int>?
|
||||
@@ -66,5 +72,4 @@ struct ShadowTableChanges {
|
||||
self.moves = moves
|
||||
self.rowChanges = rowChanges
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user