Merge branch 'ios-release'

This commit is contained in:
Maurice Parker 2020-03-28 16:51:50 -05:00
commit 532db0b640
8 changed files with 122 additions and 56 deletions

View File

@ -17,7 +17,7 @@ jobs:
matrix:
run-config:
- { scheme: 'NetNewsWire', destination: 'platform=macOS'}
- { scheme: 'NetNewsWire-iOS', destination: 'platform=iOS Simulator,OS=13.0,name=iPhone 11' }
- { scheme: 'NetNewsWire-iOS', destination: 'platform=iOS Simulator,OS=13.4,name=iPhone 11' }
steps:
- name: Checkout Project
@ -25,8 +25,11 @@ jobs:
with:
submodules: recursive
- name: List Available Applications
run: ls /Applications
- name: Switch to Xcode 11
run: sudo xcode-select -s /Applications/Xcode_11.app
run: sudo xcode-select -s /Applications/Xcode_11.4.app
- name: Show Build Version
run: xcodebuild -version

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -57,7 +57,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="o06-fe-i3S">
<rect key="frame" x="20" y="11.5" width="334" height="21"/>
<rect key="frame" x="20" y="11" width="334" height="22"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
</textField>
@ -206,7 +206,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Yl1-R6-xZi">
<rect key="frame" x="20" y="11.5" width="334" height="21"/>
<rect key="frame" x="20" y="11" width="334" height="22"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="words"/>
</textField>
@ -291,7 +291,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vJa-NN-yjR">
<rect key="frame" x="20" y="11.5" width="334" height="21"/>
<rect key="frame" x="20" y="11" width="334" height="22"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
</textField>
@ -315,7 +315,7 @@
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TfW-wf-V06">
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TfW-wf-V06">
<rect key="frame" x="311" y="5.5" width="43" height="33"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Show"/>

View File

@ -12,7 +12,7 @@ import Account
import RSTree
protocol MasterFeedTableViewCellDelegate: class {
func disclosureSelected(_ sender: MasterFeedTableViewCell, expanding: Bool)
func masterFeedTableViewCellDisclosureDidToggle(_ sender: MasterFeedTableViewCell, expanding: Bool)
}
class MasterFeedTableViewCell : VibrantTableViewCell {
@ -149,7 +149,7 @@ class MasterFeedTableViewCell : VibrantTableViewCell {
@objc func buttonPressed(_ sender: UIButton) {
if isDisclosureAvailable {
setDisclosure(isExpanded: !isDisclosureExpanded, animated: true)
delegate?.disclosureSelected(self, expanding: isDisclosureExpanded)
delegate?.masterFeedTableViewCellDisclosureDidToggle(self, expanding: isDisclosureExpanded)
}
}
@ -181,6 +181,9 @@ private extension MasterFeedTableViewCell {
disclosureButton?.tintColor = AppAssets.controlBackgroundColor
disclosureButton?.imageView?.contentMode = .center
disclosureButton?.imageView?.clipsToBounds = false
if #available(iOS 13.4, *) {
disclosureButton?.addInteraction(UIPointerInteraction())
}
addSubviewAtInit(disclosureButton!)
}

View File

@ -8,8 +8,14 @@
import UIKit
protocol MasterFeedTableViewSectionHeaderDelegate {
func masterFeedTableViewSectionHeaderDisclosureDidToggle(_ sender: MasterFeedTableViewSectionHeader)
}
class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
var delegate: MasterFeedTableViewSectionHeaderDelegate?
override var accessibilityLabel: String? {
set {}
get {
@ -66,12 +72,16 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
}()
private let unreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero)
private var disclosureView: UIImageView = {
let iView = NonIntrinsicImageView()
iView.tintColor = UIColor.tertiaryLabel
iView.image = AppAssets.disclosureImage
iView.contentMode = .center
return iView
private lazy var disclosureButton: UIButton = {
let button = NonIntrinsicButton()
button.tintColor = UIColor.tertiaryLabel
button.setImage(AppAssets.disclosureImage, for: .normal)
button.contentMode = .center
if #available(iOS 13.4, *) {
button.addInteraction(UIPointerInteraction())
}
button.addTarget(self, action: #selector(toggleDisclosure), for: .touchUpInside)
return button
}()
private let topSeparatorView: UIView = {
@ -115,10 +125,14 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
private extension MasterFeedTableViewSectionHeader {
@objc func toggleDisclosure() {
delegate?.masterFeedTableViewSectionHeaderDisclosureDidToggle(self)
}
func commonInit() {
addSubviewAtInit(unreadCountView)
addSubviewAtInit(titleView)
addSubviewAtInit(disclosureView)
addSubviewAtInit(disclosureButton)
updateExpandedState(animate: false)
addBackgroundView()
addSubviewAtInit(topSeparatorView)
@ -136,9 +150,9 @@ private extension MasterFeedTableViewSectionHeader {
withDuration: duration,
animations: {
if self.disclosureExpanded {
self.disclosureView.transform = CGAffineTransform(rotationAngle: 1.570796)
self.disclosureButton.transform = CGAffineTransform(rotationAngle: 1.570796)
} else {
self.disclosureView.transform = CGAffineTransform(rotationAngle: 0)
self.disclosureButton.transform = CGAffineTransform(rotationAngle: 0)
}
}, completion: { _ in
if !self.isLastSection && !self.disclosureExpanded {
@ -167,7 +181,7 @@ private extension MasterFeedTableViewSectionHeader {
func layoutWith(_ layout: MasterFeedTableViewSectionHeaderLayout) {
titleView.setFrameIfNotEqual(layout.titleRect)
unreadCountView.setFrameIfNotEqual(layout.unreadCountRect)
disclosureView.setFrameIfNotEqual(layout.disclosureButtonRect)
disclosureButton.setFrameIfNotEqual(layout.disclosureButtonRect)
let top = CGRect(x: safeAreaInsets.left, y: 0, width: frame.width - safeAreaInsets.right - safeAreaInsets.left, height: 0.33)
topSeparatorView.setFrameIfNotEqual(top)

View File

@ -158,6 +158,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
}
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! MasterFeedTableViewSectionHeader
headerView.delegate = self
headerView.name = nameProvider.nameForDisplay
guard let sectionNode = coordinator.rootNode.childAtIndex(section) else {
@ -386,24 +387,10 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
}
@objc func toggleSectionHeader(_ sender: UITapGestureRecognizer) {
guard let sectionIndex = sender.view?.tag,
let sectionNode = coordinator.rootNode.childAtIndex(sectionIndex),
let headerView = sender.view as? MasterFeedTableViewSectionHeader
else {
return
guard let headerView = sender.view as? MasterFeedTableViewSectionHeader else {
return
}
if coordinator.isExpanded(sectionNode) {
headerView.disclosureExpanded = false
coordinator.collapse(sectionNode)
self.applyChanges(animated: true)
} else {
headerView.disclosureExpanded = true
coordinator.expand(sectionNode)
self.applyChanges(animated: true)
}
toggle(headerView)
}
@objc func refreshAccounts(_ sender: Any) {
@ -556,11 +543,21 @@ extension MasterFeedViewController: UIContextMenuInteractionDelegate {
}
}
// MARK: MasterFeedTableViewSectionHeaderDelegate
extension MasterFeedViewController: MasterFeedTableViewSectionHeaderDelegate {
func masterFeedTableViewSectionHeaderDisclosureDidToggle(_ sender: MasterFeedTableViewSectionHeader) {
toggle(sender)
}
}
// MARK: MasterTableViewCellDelegate
extension MasterFeedViewController: MasterFeedTableViewCellDelegate {
func disclosureSelected(_ sender: MasterFeedTableViewCell, expanding: Bool) {
func masterFeedTableViewCellDisclosureDidToggle(_ sender: MasterFeedTableViewCell, expanding: Bool) {
if expanding {
expand(sender)
} else {
@ -753,6 +750,22 @@ private extension MasterFeedViewController {
return nil
}
func toggle(_ headerView: MasterFeedTableViewSectionHeader) {
guard let sectionNode = coordinator.rootNode.childAtIndex(headerView.tag) else {
return
}
if coordinator.isExpanded(sectionNode) {
headerView.disclosureExpanded = false
coordinator.collapse(sectionNode)
self.applyChanges(animated: true)
} else {
headerView.disclosureExpanded = true
coordinator.expand(sectionNode)
self.applyChanges(animated: true)
}
}
func expand(_ cell: MasterFeedTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell), let node = dataSource.itemIdentifier(for: indexPath) else {
return

View File

@ -13,5 +13,38 @@ class MasterTimelineTitleView: UIView {
@IBOutlet weak var iconView: IconView!
@IBOutlet weak var label: UILabel!
@IBOutlet weak var unreadCountView: MasterTimelineUnreadCountView!
@available(iOS 13.4, *)
private lazy var pointerInteraction: UIPointerInteraction = {
UIPointerInteraction(delegate: self)
}()
func buttonize() {
heightAnchor.constraint(equalToConstant: 40.0).isActive = true
accessibilityTraits = .button
if #available(iOS 13.4, *) {
addInteraction(pointerInteraction)
}
}
func debuttonize() {
accessibilityTraits.remove(.button)
if #available(iOS 13.4, *) {
removeInteraction(pointerInteraction)
}
}
}
extension MasterTimelineTitleView: UIPointerInteractionDelegate {
@available(iOS 13.4, *)
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
var rect = self.frame
rect.origin.x = rect.origin.x - 10
rect.size.width = rect.width + 20
return UIPointerStyle(effect: .automatic(UITargetedPreview(view: self)), shape: .roundedRect(rect))
}
}

View File

@ -564,12 +564,11 @@ private extension MasterTimelineViewController {
updateTitleUnreadCount()
if coordinator.timelineFeed is WebFeed {
titleView.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
titleView.buttonize()
titleView.addGestureRecognizer(feedTapGestureRecognizer)
titleView.accessibilityTraits = .button
} else {
titleView.debuttonize()
titleView.removeGestureRecognizer(feedTapGestureRecognizer)
titleView.accessibilityTraits.remove(.button)
}
navigationItem.titleView = titleView

View File

@ -61,7 +61,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
private var wasRootSplitViewControllerCollapsed = false
private let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5)
private let rebuildBackingStoresWithMergeQueue = CoalescingQueue(name: "Rebuild The Backing Stores by Merging", interval: 1.0)
private let rebuildBackingStoresQueue = CoalescingQueue(name: "Rebuild The Backing Stores", interval: 1.0)
private var fetchSerialNumber = 0
private let fetchRequestQueue = FetchRequestQueue()
@ -441,12 +441,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
return
}
// If we are filtering reads, the new unread count is greater than 1, and the feed isn't shown then continue
guard let feed = note.object as? Feed, isReadFeedsFiltered, feed.unreadCount > 0, !shadowTableContains(feed) else {
return
}
rebuildBackingStoresWithMergeQueue.add(self, #selector(rebuildBackingStoresWithMerge))
queueRebuildBackingStores()
}
@objc func statusesDidChange(_ note: Notification) {
@ -465,7 +460,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
@objc func batchUpdateDidPerform(_ notification: Notification) {
rebuildBackingStoresWithMerge()
rebuildBackingStores()
}
@objc func displayNameDidChange(_ note: Notification) {
@ -541,7 +536,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
@objc func downloadArticlesDidUpdateUnreadCounts(_ note: Notification) {
rebuildBackingStoresWithMerge()
rebuildBackingStores()
}
@objc func accountDidDownloadArticles(_ note: Notification) {
@ -568,7 +563,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
func suspend() {
fetchAndMergeArticlesQueue.performCallsImmediately()
rebuildBackingStoresWithMergeQueue.performCallsImmediately()
rebuildBackingStoresQueue.performCallsImmediately()
fetchRequestQueue.cancelAllRequests()
}
@ -745,6 +740,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} else {
setTimelineFeed(nil, animated: false) {
if self.isReadFeedsFiltered {
self.queueRebuildBackingStores()
}
self.activityManager.invalidateSelecting()
if self.rootSplitViewController.isCollapsed && self.navControllerForTimeline().viewControllers.last is MasterTimelineViewController {
self.navControllerForTimeline().popViewController(animated: animations.contains(.navigation))
@ -1360,7 +1358,15 @@ private extension SceneCoordinator {
}
}
}
func queueRebuildBackingStores() {
rebuildBackingStoresQueue.add(self, #selector(rebuildBackingStoresWithDefaults))
}
@objc func rebuildBackingStoresWithDefaults() {
rebuildBackingStores()
}
func rebuildBackingStores(initialLoad: Bool = false, updateExpandedNodes: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
if !animatingChanges && !BatchUpdate.shared.isPerforming {
@ -1375,11 +1381,6 @@ private extension SceneCoordinator {
}
}
@objc func rebuildBackingStoresWithMerge() {
addShadowTableToFilterExceptions()
rebuildBackingStores()
}
func rebuildShadowTable() {
shadowTable = [[Node]]()