diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 9456f369b..95d4f9cfa 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -79,6 +79,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner // Configure the table tableView.dataSource = dataSource + if #available(iOS 15.0, *) { + tableView.isPrefetchingEnabled = false + } numberOfTextLines = AppDefaults.shared.timelineNumberOfLines iconSize = AppDefaults.shared.timelineIconSize resetEstimatedRowHeight() @@ -525,14 +528,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } @objc private func reloadAllVisibleCells() { - if #available(iOS 15, *) { - reconfigureCells(coordinator.articles) - } else { - let visibleArticles = tableView.indexPathsForVisibleRows!.compactMap { return dataSource.itemIdentifier(for: $0) } - reloadCells(visibleArticles) - } + let visibleArticles = tableView.indexPathsForVisibleRows!.compactMap { return dataSource.itemIdentifier(for: $0) } + reloadCells(visibleArticles) } - + private func reloadCells(_ articles: [Article]) { var snapshot = dataSource.snapshot() snapshot.reloadItems(articles) @@ -540,15 +539,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner self?.restoreSelectionIfNecessary(adjustScroll: false) } } - - private func reconfigureCells(_ articles: [Article]) { - guard #available(iOS 15, *) else { return } - var snapshot = dataSource.snapshot() - snapshot.reconfigureItems(articles) - dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in - self?.restoreSelectionIfNecessary(adjustScroll: false) - } - } // MARK: Cell Configuring diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json b/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json deleted file mode 100644 index 4bf18a9e4..000000000 --- a/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "onepassword-navbar.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "onepassword-navbar@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "onepassword-navbar@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png b/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png deleted file mode 100644 index d20bc7dfd..000000000 Binary files a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@2x.png b/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@2x.png deleted file mode 100644 index 346dc97f9..000000000 Binary files a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@2x.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png b/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png deleted file mode 100644 index 24b64962a..000000000 Binary files a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index fa2bbb429..fd60d7f54 100644 --- a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,116 +1,116 @@ { "images" : [ { - "size" : "20x20", + "filename" : "Icon_20x20@2x.png", "idiom" : "iphone", - "filename" : "icon-41.png", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { - "size" : "20x20", + "filename" : "Icon_20x20@3x.png", "idiom" : "iphone", - "filename" : "icon-60.png", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { - "size" : "29x29", + "filename" : "Icon_29x29@2x.png", "idiom" : "iphone", - "filename" : "icon-58.png", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { - "size" : "29x29", + "filename" : "Icon_29x29@3x.png", "idiom" : "iphone", - "filename" : "icon-87.png", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { - "size" : "40x40", + "filename" : "Icon_40x40@2x.png", "idiom" : "iphone", - "filename" : "icon-80.png", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { - "size" : "40x40", + "filename" : "Icon_40x40@3x.png", "idiom" : "iphone", - "filename" : "icon-121.png", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" }, { - "size" : "60x60", + "filename" : "Icon_60x60@2x.png", "idiom" : "iphone", - "filename" : "icon-120.png", - "scale" : "2x" + "scale" : "2x", + "size" : "60x60" }, { - "size" : "60x60", + "filename" : "Icon_60x60@3x.png", "idiom" : "iphone", - "filename" : "icon-180.png", - "scale" : "3x" + "scale" : "3x", + "size" : "60x60" }, { - "size" : "20x20", - "idiom" : "ipad", "filename" : "icon-20.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" }, { - "size" : "20x20", + "filename" : "Icon_20x20@2x 1.png", "idiom" : "ipad", - "filename" : "icon-42.png", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "ipad", "filename" : "icon-29.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" }, { - "size" : "29x29", + "filename" : "Icon_29x29@2x 1.png", "idiom" : "ipad", - "filename" : "icon-59.png", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { - "size" : "40x40", - "idiom" : "ipad", "filename" : "icon-40.png", - "scale" : "1x" + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" }, { - "size" : "40x40", + "filename" : "Icon_40x40@2x 1.png", "idiom" : "ipad", - "filename" : "icon-81.png", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { - "size" : "76x76", - "idiom" : "ipad", "filename" : "icon-76.png", - "scale" : "1x" - }, - { - "size" : "76x76", "idiom" : "ipad", - "filename" : "icon-152.png", - "scale" : "2x" + "scale" : "1x", + "size" : "76x76" }, { - "size" : "83.5x83.5", + "filename" : "Icon_76x76@2x.png", "idiom" : "ipad", - "filename" : "icon-167.png", - "scale" : "2x" + "scale" : "2x", + "size" : "76x76" }, { - "size" : "1024x1024", + "filename" : "Icon_83.5x83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "Icon_1024x1024.png", "idiom" : "ios-marketing", - "filename" : "icon-1024.png", - "scale" : "1x" + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_1024x1024.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_1024x1024.png new file mode 100644 index 000000000..c23104e9b Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_1024x1024.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x 1.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x 1.png new file mode 100644 index 000000000..1dadf1062 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x 1.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x.png new file mode 100644 index 000000000..1dadf1062 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@3x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@3x.png new file mode 100644 index 000000000..05b3f4a70 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_20x20@3x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x 1.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x 1.png new file mode 100644 index 000000000..b9fc738a9 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x 1.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x.png new file mode 100644 index 000000000..b9fc738a9 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@3x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@3x.png new file mode 100644 index 000000000..79e1a4011 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_29x29@3x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x 1.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x 1.png new file mode 100644 index 000000000..44fd5c3a1 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x 1.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x.png new file mode 100644 index 000000000..44fd5c3a1 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@3x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@3x.png new file mode 100644 index 000000000..373ea7096 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_40x40@3x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@2x.png new file mode 100644 index 000000000..a137eda8b Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@3x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@3x.png new file mode 100644 index 000000000..d9a7facab Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_60x60@3x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_76x76@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_76x76@2x.png new file mode 100644 index 000000000..94125bb6f Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_76x76@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_83.5x83.5@2x.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_83.5x83.5@2x.png new file mode 100644 index 000000000..7d7cb5a2e Binary files /dev/null and b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/Icon_83.5x83.5@2x.png differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png deleted file mode 100644 index c2bb5d540..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-120.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-120.png deleted file mode 100644 index 6d1a94fd9..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-120.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-121.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-121.png deleted file mode 100644 index 6d1a94fd9..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-121.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-152.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-152.png deleted file mode 100644 index b217d09c4..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-152.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-167.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-167.png deleted file mode 100644 index 4cd8fa6c0..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-167.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-180.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-180.png deleted file mode 100644 index 8c5c93b8c..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-180.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-41.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-41.png deleted file mode 100644 index 180a98b25..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-41.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-42.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-42.png deleted file mode 100644 index 180a98b25..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-42.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-58.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-58.png deleted file mode 100644 index a53d44864..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-58.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-59.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-59.png deleted file mode 100644 index a53d44864..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-59.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-60.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-60.png deleted file mode 100644 index 7a01bc978..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-60.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-80.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-80.png deleted file mode 100644 index 85289428d..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-80.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-81.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-81.png deleted file mode 100644 index 85289428d..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-81.png and /dev/null differ diff --git a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-87.png b/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-87.png deleted file mode 100644 index dd27b3ca3..000000000 Binary files a/iOS/Resources/Assets.xcassets/AppIcon.appiconset/icon-87.png and /dev/null differ diff --git a/iOS/Resources/Info.plist b/iOS/Resources/Info.plist index 81f8b60cf..1b350b77f 100644 --- a/iOS/Resources/Info.plist +++ b/iOS/Resources/Info.plist @@ -204,6 +204,8 @@ CFBundleTypeName NetNewsWire Theme + LSHandlerRank + Owner CFBundleTypeRole Viewer LSItemContentTypes diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 526eb7703..1427cb7a5 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -86,11 +86,19 @@ class SceneCoordinator: NSObject, UndoableCommandRunner { private let rebuildBackingStoresQueue = CoalescingQueue(name: "Rebuild The Backing Stores", interval: 0.5) private var fetchSerialNumber = 0 private let fetchRequestQueue = FetchRequestQueue() - + + // Which Containers are expanded private var expandedTable = Set() + + // Which Containers used to be expanded. Reset by rebuilding the Shadow Table. + private var lastExpandedTable = Set() + + // 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? private var lastSearchString = "" private var lastSearchScope: SearchScope? = nil @@ -649,12 +657,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner { } func nodeFor(_ indexPath: IndexPath) -> Node? { - guard indexPath.section < shadowTable.count && indexPath.row < shadowTable[indexPath.section].feedNodes.count else { + guard indexPath.section > -1 && + indexPath.row > -1 && + indexPath.section < shadowTable.count && + indexPath.row < shadowTable[indexPath.section].feedNodes.count else { return nil } return shadowTable[indexPath.section].feedNodes[indexPath.row].node } - + func indexPathFor(_ node: Node) -> IndexPath? { for i in 0.. Void)? = nil) { + func discloseWebFeed(_ webFeed: WebFeed, initialLoad: Bool = false, animations: Animations = [], completion: (() -> Void)? = nil) { if isSearching { masterTimelineViewController?.hideSearch() } - + guard let account = webFeed.account else { completion?() return } - + let parentFolder = account.sortedFolders?.first(where: { $0.objectIsChild(webFeed) }) - + markExpanded(account) if let parentFolder = parentFolder { markExpanded(parentFolder) } - + if let webFeedFeedID = webFeed.feedID { self.treeControllerDelegate.addFilterException(webFeedFeedID) } @@ -1135,13 +1152,14 @@ class SceneCoordinator: NSObject, UndoableCommandRunner { self.treeControllerDelegate.addFilterException(parentFolderFeedID) } - rebuildBackingStores(completion: { + rebuildBackingStores(initialLoad: initialLoad, completion: { self.treeControllerDelegate.resetFilterExceptions() - self.selectFeed(webFeed, animations: animations, completion: completion) + self.selectFeed(nil) { + self.selectFeed(webFeed, animations: animations, completion: completion) + } }) - } - + func showStatusBar() { prefersStatusBarHidden = false UIView.animate(withDuration: 0.15) { @@ -1510,10 +1528,10 @@ private extension SceneCoordinator { var newShadowTable = [(sectionID: String, feedNodes: [FeedNode])]() for i in 0..() var inserts = Set() var deletes = Set() - + let oldFeedNodes = shadowTable.first(where: { $0.sectionID == newSectionRows.sectionID })?.feedNodes ?? [FeedNode]() - + let diff = newSectionRows.feedNodes.difference(from: oldFeedNodes).inferringMoves() for change in diff { switch change { @@ -1561,15 +1580,28 @@ 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() + + 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() var inserts = Set() var deletes = Set() - + let oldSections = shadowTable.map { $0.sectionID } let newSections = newShadowTable.map { $0.sectionID } let diff = newSections.difference(from: oldSections).inferringMoves() @@ -1591,10 +1623,10 @@ private extension SceneCoordinator { } shadowTable = newShadowTable - + return ShadowTableChanges(deletes: deletes, inserts: inserts, moves: moves, rowChanges: changes) } - + func shadowTableContains(_ feed: Feed) -> Bool { for section in shadowTable { for feedNode in section.feedNodes { @@ -2258,7 +2290,7 @@ private extension SceneCoordinator { return } - self.discloseWebFeed(webFeed) { + self.discloseWebFeed(webFeed, initialLoad: true) { self.masterFeedViewController.focus() } }