This commit is contained in:
Brent Simmons 2019-05-20 14:30:08 -07:00
commit 3ae9bdb74f
18 changed files with 310 additions and 112 deletions

View File

@ -67,6 +67,21 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
public static let feeds = "feeds" // AccountDidDownloadArticles, StatusesDidChange public static let feeds = "feeds" // AccountDidDownloadArticles, StatusesDidChange
} }
public static let defaultLocalAccountName: String = {
let defaultName: String
#if os(macOS)
defaultName = NSLocalizedString("On My Mac", comment: "Account name")
#else
if UIDevice.current.userInterfaceIdiom == .pad {
defaultName = NSLocalizedString("On My iPad", comment: "Account name")
} else {
defaultName = NSLocalizedString("On My iPhone", comment: "Account name")
}
#endif
return defaultName
}()
public let accountID: String public let accountID: String
public let type: AccountType public let type: AccountType
public var nameForDisplay: String { public var nameForDisplay: String {
@ -220,15 +235,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
switch type { switch type {
case .onMyMac: case .onMyMac:
#if os(macOS) defaultName = Account.defaultLocalAccountName
defaultName = NSLocalizedString("On My Mac", comment: "Account name")
#else
if UIDevice.current.userInterfaceIdiom == .pad {
defaultName = NSLocalizedString("On My iPad", comment: "Account name")
} else {
defaultName = NSLocalizedString("On My iPhone", comment: "Account name")
}
#endif
case .feedly: case .feedly:
defaultName = "Feedly" defaultName = "Feedly"
case .feedbin: case .feedbin:

View File

@ -150,9 +150,19 @@ public final class AccountManager: UnreadCountProvider {
activeAccounts.forEach { $0.refreshAll() } activeAccounts.forEach { $0.refreshAll() }
} }
public func syncArticleStatusAll() { public func syncArticleStatusAll(completion: (() -> Void)? = nil) {
let group = DispatchGroup()
activeAccounts.forEach { $0.syncArticleStatus() }
activeAccounts.forEach {
group.enter()
$0.syncArticleStatus() {
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
completion?()
}
} }
public func anyAccountHasAtLeastOneFeed() -> Bool { public func anyAccountHasAtLeastOneFeed() -> Bool {

View File

@ -87,7 +87,7 @@ final class StatusesTable: DatabaseTable {
} }
func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> { func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> {
return fetchArticleIDs("select articleID from statuses s where userDeleted=0 and not exists (select 1 from articles a where a.articleID = s.articleID);") return fetchArticleIDs("select articleID from statuses s where (read=0 or starred=1) and userDeleted=0 and not exists (select 1 from articles a where a.articleID = s.articleID);")
} }
func fetchArticleIDs(_ sql: String) -> Set<String> { func fetchArticleIDs(_ sql: String) -> Set<String> {

View File

@ -7,6 +7,7 @@
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsAddLocalWindowController" customModule="NetNewsWire" customModuleProvider="target"> <customObject id="-2" userLabel="File's Owner" customClass="AccountsAddLocalWindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections> <connections>
<outlet property="localAccountNameTextField" destination="80D-3X-rL2" id="B6t-AS-hDh"/>
<outlet property="nameTextField" destination="f9b-FM-i8Q" id="TJl-Uc-PNg"/> <outlet property="nameTextField" destination="f9b-FM-i8Q" id="TJl-Uc-PNg"/>
<outlet property="window" destination="QvC-M9-y7g" id="ENN-6Q-J5m"/> <outlet property="window" destination="QvC-M9-y7g" id="ENN-6Q-J5m"/>
</connections> </connections>
@ -17,13 +18,13 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="398" height="205"/> <rect key="contentRect" x="196" y="240" width="398" height="205"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/> <rect key="screenRect" x="0.0" y="0.0" width="2048" height="1129"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ"> <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="398" height="205"/> <rect key="frame" x="0.0" y="0.0" width="398" height="205"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uDK-ji-zlT"> <stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uDK-ji-zlT">
<rect key="frame" x="93" y="150" width="213" height="39"/> <rect key="frame" x="95" y="150" width="209" height="39"/>
<subviews> <subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT"> <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/> <rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
@ -34,7 +35,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="accountLocal" id="9RZ-J3-ioX"/> <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="accountLocal" id="9RZ-J3-ioX"/>
</imageView> </imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2">
<rect key="frame" x="53" y="0.0" width="162" height="39"/> <rect key="frame" x="53" y="0.0" width="158" height="39"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="On My Mac" id="1d2-Mx-TKe"> <textFieldCell key="cell" lineBreakMode="clipping" title="On My Mac" id="1d2-Mx-TKe">
<font key="font" metaFont="system" size="32"/> <font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>

View File

@ -11,14 +11,21 @@ import Account
class AccountsAddLocalWindowController: NSWindowController { class AccountsAddLocalWindowController: NSWindowController {
@IBOutlet weak var nameTextField: NSTextField! @IBOutlet private weak var nameTextField: NSTextField!
@IBOutlet private weak var localAccountNameTextField: NSTextField!
private weak var hostWindow: NSWindow? private weak var hostWindow: NSWindow?
convenience init() { convenience init() {
self.init(windowNibName: NSNib.Name("AccountsAddLocal")) self.init(windowNibName: NSNib.Name("AccountsAddLocal"))
} }
override func windowDidLoad() {
super.windowDidLoad()
localAccountNameTextField.stringValue = Account.defaultLocalAccountName
}
// MARK: API // MARK: API
func runSheetOnWindow(_ hostWindow: NSWindow) { func runSheetOnWindow(_ hostWindow: NSWindow) {

View File

@ -7,6 +7,7 @@
// //
import AppKit import AppKit
import Account
class AccountsAddViewController: NSViewController { class AccountsAddViewController: NSViewController {
@ -57,7 +58,7 @@ extension AccountsAddViewController: NSTableViewDelegate {
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountsAddTableCellView { if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountsAddTableCellView {
switch row { switch row {
case 0: case 0:
cell.accountNameLabel?.stringValue = NSLocalizedString("On My Mac", comment: "Local") cell.accountNameLabel?.stringValue = Account.defaultLocalAccountName
cell.accountImageView?.image = AppImages.accountLocal cell.accountImageView?.image = AppImages.accountLocal
case 1: case 1:
cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin") cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin")

View File

@ -14,7 +14,7 @@
<scene sceneID="2Tc-JN-edX"> <scene sceneID="2Tc-JN-edX">
<objects> <objects>
<tableViewController storyboardIdentifier="AddFeedViewController" id="7aE-6a-iP7" customClass="AddFeedViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController storyboardIdentifier="AddFeedViewController" id="7aE-6a-iP7" customClass="AddFeedViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="1" sectionFooterHeight="5" id="D0S-TM-mtm"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="1" sectionFooterHeight="5" id="D0S-TM-mtm">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/> <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/> <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
@ -28,10 +28,10 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="URL" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eRp-AP-WFq"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="URL" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eRp-AP-WFq">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/> <rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/> <nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" keyboardType="URL"/> <textInputTraits key="textInputTraits" keyboardType="URL"/>
</textField> </textField>
</subviews> </subviews>
@ -50,10 +50,10 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Title (Optional)" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="u7n-VL-Ho9"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Title (Optional)" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="u7n-VL-Ho9">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/> <rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/> <nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits"/> <textInputTraits key="textInputTraits"/>
</textField> </textField>
</subviews> </subviews>
@ -76,39 +76,38 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Folder" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grZ-g6-qfm"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Folder" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grZ-g6-qfm">
<rect key="frame" x="23.999999999999996" y="15" width="48.666666666666657" height="14"/> <rect key="frame" x="19.999999999999996" y="4" width="48.666666666666657" height="35.666666666666664"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vaV-kY-CaE"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vaV-kY-CaE">
<rect key="frame" x="325" y="15" width="42" height="14"/> <rect key="frame" x="313" y="11.666666666666666" width="42" height="20.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="vaV-kY-CaE" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="grZ-g6-qfm" secondAttribute="trailing" constant="8" id="1Dk-MH-7Hw"/> <constraint firstItem="vaV-kY-CaE" firstAttribute="centerY" secondItem="grZ-g6-qfm" secondAttribute="centerY" id="4I1-oT-KFl"/>
<constraint firstItem="grZ-g6-qfm" firstAttribute="top" secondItem="sZh-wI-IW4" secondAttribute="topMargin" constant="4" id="9dF-Uj-lPA"/> <constraint firstItem="grZ-g6-qfm" firstAttribute="leading" secondItem="sZh-wI-IW4" secondAttribute="leading" constant="20" symbolic="YES" id="5tF-au-Zv8"/>
<constraint firstItem="grZ-g6-qfm" firstAttribute="leading" secondItem="sZh-wI-IW4" secondAttribute="leadingMargin" constant="8" id="NKz-GB-i4E"/> <constraint firstAttribute="trailing" secondItem="vaV-kY-CaE" secondAttribute="trailing" constant="20" symbolic="YES" id="96f-HQ-Y0z"/>
<constraint firstAttribute="bottomMargin" secondItem="vaV-kY-CaE" secondAttribute="bottom" constant="4" id="Yfx-6j-UqX"/> <constraint firstItem="vaV-kY-CaE" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="grZ-g6-qfm" secondAttribute="trailing" constant="8" id="Hzx-N0-6NB"/>
<constraint firstItem="vaV-kY-CaE" firstAttribute="trailing" secondItem="sZh-wI-IW4" secondAttribute="trailingMargin" constant="8" id="eCs-ob-UXo"/> <constraint firstItem="grZ-g6-qfm" firstAttribute="top" secondItem="sZh-wI-IW4" secondAttribute="top" constant="4" id="PSY-sG-VGF"/>
<constraint firstItem="vaV-kY-CaE" firstAttribute="top" secondItem="sZh-wI-IW4" secondAttribute="topMargin" constant="4" id="hUO-Ln-i29"/> <constraint firstAttribute="bottom" secondItem="grZ-g6-qfm" secondAttribute="bottom" constant="4" id="SwU-Ai-DiZ"/>
<constraint firstAttribute="bottomMargin" secondItem="grZ-g6-qfm" secondAttribute="bottom" constant="4" id="tu3-aF-XD6"/>
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="200" id="PiN-2i-6Dj"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="140" id="PiN-2i-6Dj">
<rect key="frame" x="0.0" y="173" width="375" height="200"/> <rect key="frame" x="0.0" y="173" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="PiN-2i-6Dj" id="sZ4-hj-gua"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="PiN-2i-6Dj" id="sZ4-hj-gua">
<rect key="frame" x="0.0" y="0.0" width="375" height="199.66666666666666"/> <rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v2n-nX-8jq"> <pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v2n-nX-8jq">
<rect key="frame" x="0.0" y="0.0" width="375" height="199.66666666666666"/> <rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
</pickerView> </pickerView>
</subviews> </subviews>
<constraints> <constraints>
@ -242,7 +241,7 @@
<scene sceneID="m7L-uI-ghq"> <scene sceneID="m7L-uI-ghq">
<objects> <objects>
<tableViewController storyboardIdentifier="AddFolderViewController" id="3dI-34-ljo" customClass="AddFolderViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController storyboardIdentifier="AddFolderViewController" id="3dI-34-ljo" customClass="AddFolderViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="1" sectionFooterHeight="5" id="7xa-gZ-zHA"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="1" sectionFooterHeight="5" id="7xa-gZ-zHA">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/> <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/> <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
@ -256,10 +255,10 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="lZK-wx-jbo"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="lZK-wx-jbo">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/> <rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/> <nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits"/> <textInputTraits key="textInputTraits"/>
</textField> </textField>
</subviews> </subviews>
@ -282,38 +281,38 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YRf-I7-nkL"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YRf-I7-nkL">
<rect key="frame" x="20" y="4" width="64" height="35.666666666666664"/> <rect key="frame" x="20" y="4" width="64" height="35.666666666666664"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mxj-Bw-Jfx"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mxj-Bw-Jfx">
<rect key="frame" x="325" y="15" width="42" height="14"/> <rect key="frame" x="313" y="11.666666666666666" width="42" height="20.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="YRf-I7-nkL" firstAttribute="top" secondItem="y2g-dW-fPZ" secondAttribute="top" constant="4" id="7ey-y0-Aef"/> <constraint firstItem="YRf-I7-nkL" firstAttribute="top" secondItem="y2g-dW-fPZ" secondAttribute="top" constant="4" id="7ey-y0-Aef"/>
<constraint firstItem="mxj-Bw-Jfx" firstAttribute="trailing" secondItem="y2g-dW-fPZ" secondAttribute="trailingMargin" constant="8" id="Aip-HI-gf6"/> <constraint firstItem="mxj-Bw-Jfx" firstAttribute="centerY" secondItem="YRf-I7-nkL" secondAttribute="centerY" id="FDk-71-7yD"/>
<constraint firstAttribute="bottomMargin" secondItem="mxj-Bw-Jfx" secondAttribute="bottom" constant="4" id="KY1-7G-Jh8"/> <constraint firstAttribute="trailing" secondItem="mxj-Bw-Jfx" secondAttribute="trailing" constant="20" symbolic="YES" id="fIA-Rb-SEi"/>
<constraint firstAttribute="bottom" secondItem="YRf-I7-nkL" secondAttribute="bottom" constant="4" id="fcs-rL-KrO"/> <constraint firstAttribute="bottom" secondItem="YRf-I7-nkL" secondAttribute="bottom" constant="4" id="fcs-rL-KrO"/>
<constraint firstItem="mxj-Bw-Jfx" firstAttribute="top" secondItem="y2g-dW-fPZ" secondAttribute="topMargin" constant="4" id="pHr-Ke-jZ0"/> <constraint firstItem="mxj-Bw-Jfx" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="YRf-I7-nkL" secondAttribute="trailing" constant="8" id="rQ8-3l-2gz"/>
<constraint firstItem="YRf-I7-nkL" firstAttribute="leading" secondItem="y2g-dW-fPZ" secondAttribute="leading" constant="20" symbolic="YES" id="xBK-tz-P2j"/> <constraint firstItem="YRf-I7-nkL" firstAttribute="leading" secondItem="y2g-dW-fPZ" secondAttribute="leading" constant="20" symbolic="YES" id="xBK-tz-P2j"/>
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="200" id="zRi-p6-4KU"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="140" id="zRi-p6-4KU">
<rect key="frame" x="0.0" y="129" width="375" height="200"/> <rect key="frame" x="0.0" y="129" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zRi-p6-4KU" id="wek-Qh-OXr"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zRi-p6-4KU" id="wek-Qh-OXr">
<rect key="frame" x="0.0" y="0.0" width="375" height="199.66666666666666"/> <rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eGY-V8-gzJ"> <pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eGY-V8-gzJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="199.66666666666666"/> <rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
</pickerView> </pickerView>
</subviews> </subviews>
<constraints> <constraints>

View File

@ -23,6 +23,8 @@ protocol AddContainerViewControllerChildDelegate: UIViewController {
class AddContainerViewController: UIViewController { class AddContainerViewController: UIViewController {
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
@IBOutlet weak var cancelButton: UIBarButtonItem! @IBOutlet weak var cancelButton: UIBarButtonItem!
@IBOutlet weak var activityIndicatorView: UIActivityIndicatorView! @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
@IBOutlet weak var addButton: UIBarButtonItem! @IBOutlet weak var addButton: UIBarButtonItem!

View File

@ -14,12 +14,15 @@ import RSParser
class AddFeedViewController: UITableViewController, AddContainerViewControllerChild { class AddFeedViewController: UITableViewController, AddContainerViewControllerChild {
@IBOutlet weak var urlTextField: UITextField! @IBOutlet private weak var urlTextField: UITextField!
@IBOutlet weak var nameTextField: UITextField! @IBOutlet private weak var nameTextField: UITextField!
@IBOutlet weak var folderPickerView: UIPickerView! @IBOutlet private weak var folderPickerView: UIPickerView!
@IBOutlet weak var folderLabel: UILabel! @IBOutlet private weak var folderLabel: UILabel!
private var pickerData: AddFeedFolderPickerData! private lazy var pickerData: AddFeedFolderPickerData = AddFeedFolderPickerData()
private var shouldDisplayPicker: Bool {
return pickerData.containerNames.count > 1
}
private var userCancelled = false private var userCancelled = false
@ -34,19 +37,24 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
urlTextField.autocorrectionType = .no urlTextField.autocorrectionType = .no
urlTextField.autocapitalizationType = .none urlTextField.autocapitalizationType = .none
urlTextField.text = initialFeed urlTextField.text = initialFeed
urlTextField.delegate = self
if initialFeed != nil { if initialFeed != nil {
delegate?.readyToAdd(state: true) delegate?.readyToAdd(state: true)
} }
nameTextField.text = initialFeedName nameTextField.text = initialFeedName
nameTextField.delegate = self
folderLabel.text = pickerData.containerNames.first
if shouldDisplayPicker {
folderPickerView.dataSource = self
folderPickerView.delegate = self
folderPickerView.showsSelectionIndicator = true
} else {
folderPickerView.isHidden = true
}
pickerData = AddFeedFolderPickerData()
folderPickerView.dataSource = self
folderPickerView.delegate = self
folderPickerView.showsSelectionIndicator = true
folderLabel.text = pickerData.containerNames[0]
// I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it. // I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it.
tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0) tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0)
@ -117,6 +125,16 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
delegate?.readyToAdd(state: urlTextField.text?.rs_stringMayBeURL() ?? false) delegate?.readyToAdd(state: urlTextField.text?.rs_stringMayBeURL() ?? false)
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
if section == 1 && !shouldDisplayPicker {
return defaultNumberOfRows - 1
}
return defaultNumberOfRows
}
} }
extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate { extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate {
@ -200,3 +218,12 @@ private extension AddFeedViewController {
} }
} }
extension AddFeedViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@ -12,9 +12,13 @@ import RSCore
class AddFolderViewController: UITableViewController, AddContainerViewControllerChild { class AddFolderViewController: UITableViewController, AddContainerViewControllerChild {
@IBOutlet weak var nameTextField: UITextField! @IBOutlet private weak var nameTextField: UITextField!
@IBOutlet weak var accountLabel: UILabel! @IBOutlet private weak var accountLabel: UILabel!
@IBOutlet weak var accountPickerView: UIPickerView! @IBOutlet private weak var accountPickerView: UIPickerView!
private var shouldDisplayPicker: Bool {
return accounts.count > 1
}
private var accounts: [Account]! private var accounts: [Account]!
@ -25,10 +29,16 @@ class AddFolderViewController: UITableViewController, AddContainerViewController
super.viewDidLoad() super.viewDidLoad()
accounts = AccountManager.shared.sortedActiveAccounts accounts = AccountManager.shared.sortedActiveAccounts
nameTextField.delegate = self
accountLabel.text = (accounts[0] as DisplayNameProvider).nameForDisplay accountLabel.text = (accounts[0] as DisplayNameProvider).nameForDisplay
accountPickerView.dataSource = self if shouldDisplayPicker {
accountPickerView.delegate = self accountPickerView.dataSource = self
accountPickerView.delegate = self
} else {
accountPickerView.isHidden = true
}
// I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it. // I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it.
tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0) tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0)
@ -53,6 +63,14 @@ class AddFolderViewController: UITableViewController, AddContainerViewController
delegate?.readyToAdd(state: !(nameTextField.text?.isEmpty ?? false)) delegate?.readyToAdd(state: !(nameTextField.text?.isEmpty ?? false))
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
if section == 1 && !shouldDisplayPicker {
return defaultNumberOfRows - 1
}
return defaultNumberOfRows
}
} }
extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate { extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate {
@ -74,3 +92,12 @@ extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate
} }
} }
extension AddFolderViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@ -18,8 +18,20 @@ var appDelegate: AppDelegate!
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate, UnreadCountProvider { class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate, UnreadCountProvider {
private var backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid private var refreshBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
private var syncBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
var syncTimer: ArticleStatusSyncTimer?
var shuttingDown = false {
didSet {
if shuttingDown {
syncTimer?.shuttingDown = shuttingDown
syncTimer?.invalidate()
}
}
}
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "application") var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "application")
var window: UIWindow? var window: UIWindow?
@ -93,7 +105,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
} }
updateBackgroundRefreshInterval() updateBackgroundRefreshInterval()
syncTimer = ArticleStatusSyncTimer()
#if DEBUG
syncTimer!.update()
#endif
return true return true
} }
@ -123,12 +141,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
} }
func applicationDidEnterBackground(_ application: UIApplication) { func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. syncTimer?.invalidate()
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
let completeProcessing = { [unowned self] in
UIApplication.shared.endBackgroundTask(self.syncBackgroundUpdateTask)
self.syncBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
}
DispatchQueue.global(qos: .background).async { [unowned self] in
self.syncBackgroundUpdateTask = UIApplication.shared.beginBackgroundTask {
completeProcessing()
os_log("Accounts sync processing terminated for running too long.", log: self.log, type: .info)
}
DispatchQueue.main.async {
AccountManager.shared.syncArticleStatusAll() {
completeProcessing()
}
}
}
} }
func applicationWillEnterForeground(_ application: UIApplication) { func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. AccountManager.shared.syncArticleStatusAll()
syncTimer?.update()
} }
func applicationDidBecomeActive(_ application: UIApplication) { func applicationDidBecomeActive(_ application: UIApplication) {
@ -145,7 +184,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
} }
func applicationWillTerminate(_ application: UIApplication) { func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. shuttingDown = true
} }
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
@ -156,8 +195,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
let completeProcessing = { [unowned self] in let completeProcessing = { [unowned self] in
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask) UIApplication.shared.endBackgroundTask(self.refreshBackgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid self.refreshBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
if startingUnreadCount < self.unreadCount { if startingUnreadCount < self.unreadCount {
self.sendReceivedArticlesUserNotification(newArticleCount: self.unreadCount - startingUnreadCount) self.sendReceivedArticlesUserNotification(newArticleCount: self.unreadCount - startingUnreadCount)
@ -170,7 +209,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
DispatchQueue.global(qos: .background).async { [unowned self] in DispatchQueue.global(qos: .background).async { [unowned self] in
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask { self.refreshBackgroundUpdateTask = UIApplication.shared.beginBackgroundTask {
completeProcessing() completeProcessing()
os_log("Accounts refresh processing terminated for running too long.", log: self.log, type: .info) os_log("Accounts refresh processing terminated for running too long.", log: self.log, type: .info)
} }

View File

@ -259,6 +259,7 @@
</navigationItem> </navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/> <simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections> <connections>
<outlet property="addNewItemButton" destination="yo6-w4-SfI" id="NbL-RH-N4J"/>
<outlet property="markAllAsReadButton" destination="ddj-Ya-Wol" id="jjr-OK-4zl"/> <outlet property="markAllAsReadButton" destination="ddj-Ya-Wol" id="jjr-OK-4zl"/>
</connections> </connections>
</tableViewController> </tableViewController>

View File

@ -14,7 +14,8 @@ import RSTree
class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunner { class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunner {
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem! @IBOutlet private weak var markAllAsReadButton: UIBarButtonItem!
@IBOutlet private weak var addNewItemButton: UIBarButtonItem!
var undoableCommands = [UndoableCommand]() var undoableCommands = [UndoableCommand]()
@ -35,7 +36,9 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(backingStoresDidRebuild(_:)), name: .BackingStoresDidRebuild, object: navState) NotificationCenter.default.addObserver(self, selector: #selector(backingStoresDidRebuild(_:)), name: .BackingStoresDidRebuild, object: navState)
NotificationCenter.default.addObserver(self, selector: #selector(masterSelectionDidChange(_:)), name: .MasterSelectionDidChange, object: navState) NotificationCenter.default.addObserver(self, selector: #selector(masterSelectionDidChange(_:)), name: .MasterSelectionDidChange, object: navState)
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
@ -48,7 +51,7 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed clearsSelectionOnViewWillAppear = true
navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds") navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds")
super.viewWillAppear(animated) super.viewWillAppear(animated)
} }
@ -140,7 +143,15 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
} }
} }
@objc func accountsDidChange(_ notification: Notification) {
updateUI()
}
@objc func accountStateDidChange(_ notification: Notification) {
updateUI()
}
@objc func masterSelectionDidChange(_ note: Notification) { @objc func masterSelectionDidChange(_ note: Notification) {
if let indexPath = navState.currentMasterIndexPath { if let indexPath = navState.currentMasterIndexPath {
if tableView.indexPathForSelectedRow != indexPath { if tableView.indexPathForSelectedRow != indexPath {
@ -433,8 +444,10 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
@IBAction func add(_ sender: UIBarButtonItem) { @IBAction func add(_ sender: UIBarButtonItem) {
let addViewController = UIStoryboard.add.instantiateInitialViewController()! let addViewController = UIStoryboard.add.instantiateInitialViewController()!
addViewController.modalPresentationStyle = .popover addViewController.modalPresentationStyle = .formSheet
addViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay
addViewController.popoverPresentationController?.barButtonItem = sender addViewController.popoverPresentationController?.barButtonItem = sender
self.present(addViewController, animated: true) self.present(addViewController, animated: true)
} }
@ -596,6 +609,7 @@ private extension MasterFeedViewController {
func updateUI() { func updateUI() {
markAllAsReadButton.isEnabled = navState.isAnyUnreadAvailable markAllAsReadButton.isEnabled = navState.isAnyUnreadAvailable
addNewItemButton.isEnabled = !AccountManager.shared.activeAccounts.isEmpty
} }
func configureCellsForRepresentedObject(_ representedObject: AnyObject) { func configureCellsForRepresentedObject(_ representedObject: AnyObject) {

View File

@ -6,6 +6,7 @@
// Copyright © 2019 Ranchero Software. All rights reserved. // Copyright © 2019 Ranchero Software. All rights reserved.
// //
import Account
import UIKit import UIKit
protocol AddAccountDismissDelegate: UIViewController { protocol AddAccountDismissDelegate: UIViewController {
@ -14,6 +15,14 @@ protocol AddAccountDismissDelegate: UIViewController {
class AddAccountViewController: UITableViewController, AddAccountDismissDelegate { class AddAccountViewController: UITableViewController, AddAccountDismissDelegate {
@IBOutlet private weak var localAccountNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
localAccountNameLabel.text = Account.defaultLocalAccountName
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row { switch indexPath.row {
case 0: case 0:

View File

@ -11,9 +11,18 @@ import Account
class AddLocalAccountViewController: UIViewController { class AddLocalAccountViewController: UIViewController {
@IBOutlet private weak var localAccountNameLabel: UILabel!
@IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var nameTextField: UITextField!
weak var delegate: AddAccountDismissDelegate?
weak var delegate: AddAccountDismissDelegate?
override func viewDidLoad() {
super.viewDidLoad()
localAccountNameLabel.text = Account.defaultLocalAccountName
nameTextField.delegate = self
}
@IBAction func cancel(_ sender: Any) { @IBAction func cancel(_ sender: Any) {
dismiss(animated: true) dismiss(animated: true)
} }
@ -26,3 +35,12 @@ class AddLocalAccountViewController: UIViewController {
} }
} }
extension AddLocalAccountViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@ -21,6 +21,7 @@ class DetailAccountViewController: UITableViewController {
guard let account = account else { return } guard let account = account else { return }
nameTextField.text = account.name nameTextField.text = account.name
nameTextField.delegate = self
activeSwitch.isOn = account.isActive activeSwitch.isOn = account.isActive
} }
@ -28,6 +29,10 @@ class DetailAccountViewController: UITableViewController {
account?.name = nameTextField.text account?.name = nameTextField.text
account?.isActive = activeSwitch.isOn account?.isActive = activeSwitch.isOn
} }
}
extension DetailAccountViewController {
override func numberOfSections(in tableView: UITableView) -> Int { override func numberOfSections(in tableView: UITableView) -> Int {
if account == AccountManager.shared.defaultAccount { if account == AccountManager.shared.defaultAccount {
@ -46,6 +51,14 @@ class DetailAccountViewController: UITableViewController {
return cell return cell
} }
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
if indexPath.section == 1 {
return true
}
return false
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 { if indexPath.section == 1 {
deleteAccount() deleteAccount()
@ -79,3 +92,12 @@ private extension DetailAccountViewController {
} }
} }
extension DetailAccountViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@ -127,12 +127,29 @@
</subviews> </subviews>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dXN-Mw-yf2" style="IBUITableViewCellStyleDefault" id="F0L-Ut-reX">
<rect key="frame" x="0.0" y="375.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F0L-Ut-reX" id="5SX-M2-2jR">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add NetNewsWire News Feed" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dXN-Mw-yf2">
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells> </cells>
</tableViewSection> </tableViewSection>
<tableViewSection headerTitle="Contribute" id="eai-B1-uYA"> <tableViewSection headerTitle="Contribute" id="eai-B1-uYA">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4GG-r8-1Pi" style="IBUITableViewCellStyleDefault" id="NqU-08-VRI"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4GG-r8-1Pi" style="IBUITableViewCellStyleDefault" id="NqU-08-VRI">
<rect key="frame" x="0.0" y="431.5" width="414" height="44"/> <rect key="frame" x="0.0" y="475.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="NqU-08-VRI" id="jzn-xS-PHJ"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="NqU-08-VRI" id="jzn-xS-PHJ">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
@ -153,7 +170,7 @@
<tableViewSection headerTitle="TIMELINE" id="9Pk-Y8-JVJ"> <tableViewSection headerTitle="TIMELINE" id="9Pk-Y8-JVJ">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="MpA-w1-Wwh"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="MpA-w1-Wwh">
<rect key="frame" x="0.0" y="531.5" width="414" height="44"/> <rect key="frame" x="0.0" y="575.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MpA-w1-Wwh" id="GhU-ib-Mz8"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MpA-w1-Wwh" id="GhU-ib-Mz8">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
@ -183,7 +200,7 @@
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="B5l-Qp-6Gm" detailTextLabel="u7B-tj-cw8" style="IBUITableViewCellStyleValue1" id="air-4O-D7p"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="B5l-Qp-6Gm" detailTextLabel="u7B-tj-cw8" style="IBUITableViewCellStyleValue1" id="air-4O-D7p">
<rect key="frame" x="0.0" y="575.5" width="414" height="44"/> <rect key="frame" x="0.0" y="619.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="air-4O-D7p" id="76S-R5-xwM"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="air-4O-D7p" id="76S-R5-xwM">
<rect key="frame" x="0.0" y="0.0" width="376" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="376" height="43.5"/>
@ -211,7 +228,7 @@
<tableViewSection headerTitle="DATABASE" id="hAC-uA-RbS"> <tableViewSection headerTitle="DATABASE" id="hAC-uA-RbS">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="qur-cL-wrM" detailTextLabel="qIl-N6-6wQ" style="IBUITableViewCellStyleValue1" id="z1J-VF-St0"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="qur-cL-wrM" detailTextLabel="qIl-N6-6wQ" style="IBUITableViewCellStyleValue1" id="z1J-VF-St0">
<rect key="frame" x="0.0" y="675.5" width="414" height="44"/> <rect key="frame" x="0.0" y="719.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="z1J-VF-St0" id="Y8U-Ka-GeZ"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="z1J-VF-St0" id="Y8U-Ka-GeZ">
<rect key="frame" x="0.0" y="0.0" width="376" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="376" height="43.5"/>
@ -234,23 +251,6 @@
</subviews> </subviews>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dXN-Mw-yf2" style="IBUITableViewCellStyleDefault" id="F0L-Ut-reX">
<rect key="frame" x="0.0" y="719.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F0L-Ut-reX" id="5SX-M2-2jR">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add NetNewsWire News Feed" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dXN-Mw-yf2">
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P">
<rect key="frame" x="0.0" y="763.5" width="414" height="44"/> <rect key="frame" x="0.0" y="763.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
@ -337,7 +337,7 @@
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Xs-17-Ubp"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="right" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Xs-17-Ubp">
<rect key="frame" x="64" y="0.0" width="310" height="21"/> <rect key="frame" x="64" y="0.0" width="310" height="21"/>
<nil key="textColor"/> <nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
@ -508,6 +508,9 @@
<outlet property="delegate" destination="b00-4A-bV6" id="FKM-rN-deu"/> <outlet property="delegate" destination="b00-4A-bV6" id="FKM-rN-deu"/>
</connections> </connections>
</tableView> </tableView>
<connections>
<outlet property="localAccountNameLabel" destination="116-rt-msI" id="h6M-5V-392"/>
</connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="kmn-Q7-rga" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="kmn-Q7-rga" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
@ -572,6 +575,7 @@
</barButtonItem> </barButtonItem>
</navigationItem> </navigationItem>
<connections> <connections>
<outlet property="localAccountNameLabel" destination="Tdm-Ge-uc6" id="48O-IT-xXw"/>
<outlet property="nameTextField" destination="2dx-hK-hxL" id="t5V-3x-3vu"/> <outlet property="nameTextField" destination="2dx-hK-hxL" id="t5V-3x-3vu"/>
</connections> </connections>
</viewController> </viewController>

View File

@ -58,6 +58,13 @@ class SettingsViewController: UITableViewController {
switch section { switch section {
case 0: case 0:
return AccountManager.shared.accounts.count + 1 return AccountManager.shared.accounts.count + 1
case 1:
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
if AccountManager.shared.activeAccounts.isEmpty {
// Hide the add NetNewsWire feed row if they don't have any active accounts
return defaultNumberOfRows - 1
}
return defaultNumberOfRows
default: default:
return super.tableView(tableView, numberOfRowsInSection: section) return super.tableView(tableView, numberOfRowsInSection: section)
} }
@ -116,8 +123,12 @@ class SettingsViewController: UITableViewController {
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:]) UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:])
case 3: case 3:
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:]) UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:])
default: case 4:
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:]) UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:])
case 5:
addFeed()
default:
UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:])
} }
case 2: case 2:
UIApplication.shared.open(URL(string: "https://appcamp4girls.com/contribute/")!, options: [:]) UIApplication.shared.open(URL(string: "https://appcamp4girls.com/contribute/")!, options: [:])
@ -132,10 +143,8 @@ class SettingsViewController: UITableViewController {
let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self) let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self)
self.navigationController?.pushViewController(timeline, animated: true) self.navigationController?.pushViewController(timeline, animated: true)
case 1: case 1:
addFeed()
case 2:
importOPML() importOPML()
case 3: case 2:
exportOPML() exportOPML()
default: default:
print("export") print("export")
@ -227,6 +236,7 @@ private extension SettingsViewController {
let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController
let addViewController = addNavViewController.topViewController as! AddContainerViewController let addViewController = addNavViewController.topViewController as! AddContainerViewController
addNavViewController.modalPresentationStyle = .formSheet addNavViewController.modalPresentationStyle = .formSheet
addNavViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay
addViewController.initialFeed = appNewsURLString addViewController.initialFeed = appNewsURLString
addViewController.initialFeedName = "NetNewsWire News" addViewController.initialFeedName = "NetNewsWire News"