Merge branch 'main' into ios-ui-settings-localised
# Conflicts: # NetNewsWire.xcodeproj/project.pbxproj # Shared/Timer/AccountRefreshTimer.swift # iOS/Account/ReaderAPIAccountViewController.swift
@@ -15,7 +15,6 @@ import RSParser
|
||||
enum AddFeedType {
|
||||
case web
|
||||
case reddit
|
||||
case twitter
|
||||
}
|
||||
|
||||
class AddFeedViewController: UITableViewController {
|
||||
@@ -44,9 +43,6 @@ class AddFeedViewController: UITableViewController {
|
||||
case .reddit:
|
||||
navigationItem.title = NSLocalizedString("Add Reddit Feed", comment: "Add Reddit Feed")
|
||||
navigationItem.leftBarButtonItem = nil
|
||||
case .twitter:
|
||||
navigationItem.title = NSLocalizedString("Add Twitter Feed", comment: "Add Twitter Feed")
|
||||
navigationItem.leftBarButtonItem = nil
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="4Q4-Hi-Lic">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="np9-bP-Yhx">
|
||||
<objects>
|
||||
<navigationController id="4Q4-Hi-Lic" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="9e6-4b-IPV">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="q78-0w-suH" kind="relationship" relationship="rootViewController" id="xn5-zT-uXK"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="sjJ-Fz-BXf" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1254" y="173"/>
|
||||
</scene>
|
||||
<!--Select Type-->
|
||||
<scene sceneID="Fmm-TL-h7h">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="TwitterSelectTypeTableViewController" title="Select Type" id="q78-0w-suH" customClass="TwitterSelectTypeTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="SFq-R0-gSo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="Dp6-La-NeL">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="WAs-zr-8RJ" detailTextLabel="f8o-SY-a2H" style="IBUITableViewCellStyleSubtitle" id="VbH-aQ-M4H" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="18" width="374" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VbH-aQ-M4H" id="Qud-m5-1ZQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Home Timeline" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="WAs-zr-8RJ">
|
||||
<rect key="frame" x="20" y="9" width="114" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Tweets from everyone you follow" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="f8o-SY-a2H">
|
||||
<rect key="frame" x="20" y="32.5" width="198.5" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="K0b-HX-8dR" detailTextLabel="uaZ-4Q-FBS" style="IBUITableViewCellStyleSubtitle" id="jft-wJ-OVX" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="77" width="374" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jft-wJ-OVX" id="dXF-Bc-NkR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Mentions" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="K0b-HX-8dR">
|
||||
<rect key="frame" x="20" y="9" width="70.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Tweets mentioning you" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="uaZ-4Q-FBS">
|
||||
<rect key="frame" x="20" y="32.5" width="140" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Oqx-Ox-oyD" detailTextLabel="lqE-9F-jud" style="IBUITableViewCellStyleSubtitle" id="8qx-8E-cJo" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="136" width="374" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="8qx-8E-cJo" id="bk5-cB-EOT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Screen Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Oqx-Ox-oyD">
|
||||
<rect key="frame" x="20" y="9" width="102.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Tweets from another Twitter user" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="lqE-9F-jud">
|
||||
<rect key="frame" x="20" y="32.5" width="201" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Xj8-CU-C7D" detailTextLabel="SJg-RF-48S" style="IBUITableViewCellStyleSubtitle" id="vrw-y9-yJd" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="195" width="374" height="58"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="vrw-y9-yJd" id="bOx-t8-zfi">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="58"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Search" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Xj8-CU-C7D">
|
||||
<rect key="frame" x="20" y="9" width="53.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Tweets containing a #hastag or search term" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SJg-RF-48S">
|
||||
<rect key="frame" x="20" y="32.5" width="248" height="14.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="q78-0w-suH" id="fIb-JL-htF"/>
|
||||
<outlet property="delegate" destination="q78-0w-suH" id="j69-O6-ths"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Select Twitter Type" id="kjr-7P-QSh">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="xkx-QM-tXd">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="q78-0w-suH" id="3LG-Q8-Aqh"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="yI5-IG-7Sl" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-538" y="174"/>
|
||||
</scene>
|
||||
<!--Select Account-->
|
||||
<scene sceneID="rKM-ZF-73N">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="TwitterSelectAccountTableViewController" title="Select Account" id="2vd-nT-5dg" customClass="TwitterSelectAccountTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="T93-wO-GIE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="j8c-JM-nzm" style="IBUITableViewCellStyleDefault" id="vEE-Gx-Zgc" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="55.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="vEE-Gx-Zgc" id="pa0-mR-hgR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="j8c-JM-nzm">
|
||||
<rect key="frame" x="20" y="0.0" width="315" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="2vd-nT-5dg" id="GvE-oh-4gy"/>
|
||||
<outlet property="delegate" destination="2vd-nT-5dg" id="hdE-2N-0X0"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LMf-ZZ-Z1s" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="155" y="173"/>
|
||||
</scene>
|
||||
<!--Enter Detail-->
|
||||
<scene sceneID="lmR-Pm-7vI">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="TwitterEnterDetailTableViewController" title="Enter Detail" id="Eh0-p4-hVX" customClass="TwitterEnterDetailTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="76O-el-2DO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="ZkR-cP-Kvy">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="bWA-Rs-IL9">
|
||||
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="bWA-Rs-IL9" id="azg-eE-bd4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="IVd-Bz-j7J">
|
||||
<rect key="frame" x="20" y="11" width="334" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="IVd-Bz-j7J" firstAttribute="centerY" secondItem="azg-eE-bd4" secondAttribute="centerY" id="ItM-Jv-j2G"/>
|
||||
<constraint firstItem="IVd-Bz-j7J" firstAttribute="leading" secondItem="azg-eE-bd4" secondAttribute="leading" constant="20" symbolic="YES" id="ldz-j4-8kY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="IVd-Bz-j7J" secondAttribute="trailing" constant="20" symbolic="YES" id="un0-pU-AHV"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="Eh0-p4-hVX" id="eKE-xW-f3D"/>
|
||||
<outlet property="delegate" destination="Eh0-p4-hVX" id="e07-6Q-SQc"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="detailTextField" destination="IVd-Bz-j7J" id="p1f-3d-6MR"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="cp9-xU-RGq" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="836" y="173"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<systemColor name="systemGroupedBackgroundColor">
|
||||
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -1,82 +0,0 @@
|
||||
//
|
||||
// TwitterEnterDetailTableViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/23/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class TwitterEnterDetailTableViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var detailTextField: UITextField!
|
||||
|
||||
var doneBarButtonItem = UIBarButtonItem()
|
||||
var twitterFeedType: TwitterFeedType?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
doneBarButtonItem.title = NSLocalizedString("Next", comment: "Next")
|
||||
doneBarButtonItem.style = .plain
|
||||
doneBarButtonItem.target = self
|
||||
doneBarButtonItem.action = #selector(done)
|
||||
navigationItem.rightBarButtonItem = doneBarButtonItem
|
||||
|
||||
if case .screenName = twitterFeedType {
|
||||
navigationItem.title = NSLocalizedString("Enter Name", comment: "Enter Name")
|
||||
detailTextField.placeholder = NSLocalizedString("Screen Name", comment: "Screen Name")
|
||||
} else {
|
||||
navigationItem.title = NSLocalizedString("Enter Search", comment: "Enter Search")
|
||||
detailTextField.placeholder = NSLocalizedString("Search Term or #hashtag", comment: "Search Term")
|
||||
}
|
||||
|
||||
detailTextField.delegate = self
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: detailTextField)
|
||||
|
||||
updateUI()
|
||||
}
|
||||
|
||||
@objc func done() {
|
||||
guard let twitterFeedType = twitterFeedType, var text = detailTextField.text?.collapsingWhitespace else { return }
|
||||
|
||||
let url: String?
|
||||
if twitterFeedType == .screenName {
|
||||
if text.starts(with: "@") {
|
||||
text = String(text[text.index(text.startIndex, offsetBy: 1)..<text.endIndex])
|
||||
}
|
||||
url = TwitterFeedProvider.buildURL(twitterFeedType, username: nil, screenName: text, searchField: nil)?.absoluteString
|
||||
} else {
|
||||
url = TwitterFeedProvider.buildURL(twitterFeedType, username: nil, screenName: nil, searchField: text)?.absoluteString
|
||||
}
|
||||
|
||||
let addViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddWebFeedViewController") as! AddFeedViewController
|
||||
addViewController.addFeedType = .twitter
|
||||
addViewController.initialFeed = url
|
||||
navigationController?.pushViewController(addViewController, animated: true)
|
||||
}
|
||||
|
||||
@objc func textDidChange(_ note: Notification) {
|
||||
updateUI()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TwitterEnterDetailTableViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension TwitterEnterDetailTableViewController {
|
||||
|
||||
func updateUI() {
|
||||
doneBarButtonItem.isEnabled = !(detailTextField.text?.isEmpty ?? false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// TwitterSelectAccountTableViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/23/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class TwitterSelectAccountTableViewController: UITableViewController {
|
||||
|
||||
private var twitterFeedProviders = [TwitterFeedProvider]()
|
||||
|
||||
var twitterFeedType: TwitterFeedType?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
twitterFeedProviders = ExtensionPointManager.shared.activeExtensionPoints.values.compactMap { $0 as? TwitterFeedProvider }
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return twitterFeedProviders.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
cell.textLabel?.text = "@\(twitterFeedProviders[indexPath.row].screenName)"
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
guard let twitterFeedType = twitterFeedType else { return }
|
||||
|
||||
let username = twitterFeedProviders[indexPath.row].screenName
|
||||
let url = TwitterFeedProvider.buildURL(twitterFeedType, username: username, screenName: nil, searchField: nil)?.absoluteString
|
||||
|
||||
let addViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddWebFeedViewController") as! AddFeedViewController
|
||||
addViewController.addFeedType = .twitter
|
||||
addViewController.initialFeed = url
|
||||
navigationController?.pushViewController(addViewController, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// TwitterSelectTypeTableViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/23/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class TwitterSelectTypeTableViewController: UITableViewController {
|
||||
|
||||
private var twitterFeedProviders = [TwitterFeedProvider]()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
twitterFeedProviders = ExtensionPointManager.shared.activeExtensionPoints.values.compactMap { $0 as? TwitterFeedProvider }
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
if indexPath.row < 2 {
|
||||
if twitterFeedProviders.count > 1 {
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
}
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
if twitterFeedProviders.count == 1 {
|
||||
let username = twitterFeedProviders.first!.screenName
|
||||
let url = TwitterFeedProvider.buildURL(.homeTimeline, username: username, screenName: nil, searchField: nil)?.absoluteString
|
||||
pushAddFeedController(url)
|
||||
} else {
|
||||
let selectAccount = UIStoryboard.twitterAdd.instantiateController(ofType: TwitterSelectAccountTableViewController.self)
|
||||
selectAccount.twitterFeedType = .homeTimeline
|
||||
navigationController?.pushViewController(selectAccount, animated: true)
|
||||
}
|
||||
case 1:
|
||||
if twitterFeedProviders.count == 1 {
|
||||
let username = twitterFeedProviders.first!.screenName
|
||||
let url = TwitterFeedProvider.buildURL(.mentions, username: username, screenName: nil, searchField: nil)?.absoluteString
|
||||
pushAddFeedController(url)
|
||||
} else {
|
||||
let selectAccount = UIStoryboard.twitterAdd.instantiateController(ofType: TwitterSelectAccountTableViewController.self)
|
||||
selectAccount.twitterFeedType = .mentions
|
||||
navigationController?.pushViewController(selectAccount, animated: true)
|
||||
}
|
||||
case 2:
|
||||
let enterDetail = UIStoryboard.twitterAdd.instantiateController(ofType: TwitterEnterDetailTableViewController.self)
|
||||
enterDetail.twitterFeedType = .screenName
|
||||
navigationController?.pushViewController(enterDetail, animated: true)
|
||||
case 3:
|
||||
let enterDetail = UIStoryboard.twitterAdd.instantiateController(ofType: TwitterEnterDetailTableViewController.self)
|
||||
enterDetail.twitterFeedType = .search
|
||||
navigationController?.pushViewController(enterDetail, animated: true)
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension TwitterSelectTypeTableViewController {
|
||||
|
||||
func pushAddFeedController(_ url: String?) {
|
||||
let addViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddWebFeedViewController") as! AddFeedViewController
|
||||
addViewController.addFeedType = .twitter
|
||||
addViewController.initialFeed = url
|
||||
navigationController?.pushViewController(addViewController, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -113,10 +113,6 @@ struct AppAssets {
|
||||
return UIImage(named: "contextMenuReddit")!
|
||||
}()
|
||||
|
||||
static var contextMenuTwitter: UIImage = {
|
||||
return UIImage(named: "contextMenuTwitter")!
|
||||
}()
|
||||
|
||||
static var copyImage: UIImage = {
|
||||
return UIImage(systemName: "doc.on.doc")!
|
||||
}()
|
||||
@@ -133,10 +129,6 @@ struct AppAssets {
|
||||
return RSImage(named: "extensionPointReddit")!
|
||||
}()
|
||||
|
||||
static var extensionPointTwitter: UIImage = {
|
||||
return UIImage(named: "extensionPointTwitter")!
|
||||
}()
|
||||
|
||||
static var faviconTemplateImage: RSImage = {
|
||||
return RSImage(named: "faviconTemplateImage")!
|
||||
}()
|
||||
@@ -280,10 +272,6 @@ struct AppAssets {
|
||||
return UIImage(systemName: "trash")!
|
||||
}()
|
||||
|
||||
static var twitterOriginal: UIImage = {
|
||||
return UIImage(named: "twitterWhite")!.withRenderingMode(.alwaysOriginal).withTintColor(.secondaryLabel)
|
||||
}()
|
||||
|
||||
static var unreadFeedImage: IconImage {
|
||||
let image = UIImage(systemName: "largecircle.fill.circle")!
|
||||
return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: AppAssets.secondaryAccentColor.cgColor)
|
||||
|
||||
@@ -30,7 +30,7 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
|
||||
|
||||
final class AppDefaults: ObservableObject {
|
||||
|
||||
static let defaultThemeName = "Default"
|
||||
static let defaultThemeName = "NetNewsWire"
|
||||
|
||||
static let shared = AppDefaults()
|
||||
private init() {}
|
||||
@@ -60,6 +60,7 @@ final class AppDefaults: ObservableObject {
|
||||
static let addFolderAccountID = "addFolderAccountID"
|
||||
static let useSystemBrowser = "useSystemBrowser"
|
||||
static let currentThemeName = "currentThemeName"
|
||||
static let twitterDeprecationAlertShown = "twitterDeprecationAlertShown"
|
||||
}
|
||||
|
||||
let isDeveloperBuild: Bool = {
|
||||
@@ -256,6 +257,15 @@ final class AppDefaults: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
var twitterDeprecationAlertShown: Bool {
|
||||
get {
|
||||
return AppDefaults.bool(for: Key.twitterDeprecationAlertShown)
|
||||
}
|
||||
set {
|
||||
AppDefaults.setBool(for: Key.twitterDeprecationAlertShown, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
static func registerDefaults() {
|
||||
let defaults: [String : Any] = [Key.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue,
|
||||
Key.timelineGroupByFeed: false,
|
||||
|
||||
@@ -45,7 +45,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi
|
||||
return button
|
||||
}()
|
||||
|
||||
var mainControllerIdentifer = MainControllerIdentifier.article
|
||||
var mainControllerIdentifier = MainControllerIdentifier.article
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
|
||||
@@ -267,7 +267,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi
|
||||
themeActions.append(action)
|
||||
}
|
||||
|
||||
let defaultThemeAction = UIAction(title: NSLocalizedString("Default", comment: "Default"),
|
||||
let defaultThemeAction = UIAction(title: "NetNewsWire",
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
|
||||
@@ -35,16 +35,24 @@ public class AddWebFeedIntentHandler: NSObject, AddWebFeedIntentHandling {
|
||||
completion(.success(with: url))
|
||||
}
|
||||
|
||||
public func provideAccountNameOptions(for intent: AddWebFeedIntent, with completion: @escaping ([String]?, Error?) -> Void) {
|
||||
public func resolveTitle(for intent: AddWebFeedIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
|
||||
guard let title = intent.title else {
|
||||
completion(INStringResolutionResult.notRequired())
|
||||
return
|
||||
}
|
||||
completion(.success(with: title))
|
||||
}
|
||||
|
||||
public func provideAccountNameOptionsCollection(for intent: AddWebFeedIntent, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
|
||||
guard let extensionContainers = ExtensionContainersFile.read() else {
|
||||
completion(nil, AddWebFeedIntentHandlerError.communicationFailure)
|
||||
return
|
||||
}
|
||||
|
||||
let accountNames = extensionContainers.accounts.map { $0.name }
|
||||
completion(accountNames, nil)
|
||||
completion(INObjectCollection(items: accountNames as [NSString]), nil)
|
||||
}
|
||||
|
||||
|
||||
public func resolveAccountName(for intent: AddWebFeedIntent, with completion: @escaping (AddWebFeedAccountNameResolutionResult) -> Void) {
|
||||
guard let accountName = intent.accountName else {
|
||||
completion(AddWebFeedAccountNameResolutionResult.notRequired())
|
||||
@@ -78,6 +86,21 @@ public class AddWebFeedIntentHandler: NSObject, AddWebFeedIntentHandling {
|
||||
completion(folderNames, nil)
|
||||
}
|
||||
|
||||
public func provideFolderNameOptionsCollection(for intent: AddWebFeedIntent, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
|
||||
guard let extensionContainers = ExtensionContainersFile.read() else {
|
||||
completion(nil, AddWebFeedIntentHandlerError.communicationFailure)
|
||||
return
|
||||
}
|
||||
|
||||
guard let accountName = intent.accountName, let account = extensionContainers.findAccount(forName: accountName) else {
|
||||
completion(INObjectCollection(items: [NSString]()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
let folderNames = account.folders.map { $0.name }
|
||||
completion(INObjectCollection(items: folderNames as [NSString]), nil)
|
||||
}
|
||||
|
||||
public func resolveFolderName(for intent: AddWebFeedIntent, with completion: @escaping (AddWebFeedFolderNameResolutionResult) -> Void) {
|
||||
guard let accountName = intent.accountName, let folderName = intent.folderName else {
|
||||
completion(AddWebFeedFolderNameResolutionResult.notRequired())
|
||||
@@ -135,7 +158,7 @@ public class AddWebFeedIntentHandler: NSObject, AddWebFeedIntentHandling {
|
||||
return
|
||||
}
|
||||
|
||||
let request = ExtensionFeedAddRequest(name: nil, feedURL: url, destinationContainerID: containerID)
|
||||
let request = ExtensionFeedAddRequest(name: intent.title, feedURL: url, destinationContainerID: containerID)
|
||||
ExtensionFeedAddRequestFile.save(request)
|
||||
completion(AddWebFeedIntentResponse(code: .success, userActivity: nil))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
<key>INEnums</key>
|
||||
<array/>
|
||||
<key>INIntentDefinitionModelVersion</key>
|
||||
<string>1.1</string>
|
||||
<string>1.2</string>
|
||||
<key>INIntentDefinitionNamespace</key>
|
||||
<string>U6u7RF</string>
|
||||
<key>INIntentDefinitionSystemVersion</key>
|
||||
<string>19D76</string>
|
||||
<string>22A400</string>
|
||||
<key>INIntentDefinitionToolsBuildVersion</key>
|
||||
<string>11B53</string>
|
||||
<string>14B47b</string>
|
||||
<key>INIntentDefinitionToolsVersion</key>
|
||||
<string>11.2.1</string>
|
||||
<string>14.1</string>
|
||||
<key>INIntents</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -32,21 +32,10 @@
|
||||
<key>INIntentKeyParameter</key>
|
||||
<string>url</string>
|
||||
<key>INIntentLastParameterTag</key>
|
||||
<integer>4</integer>
|
||||
<integer>5</integer>
|
||||
<key>INIntentManagedParameterCombinations</key>
|
||||
<dict>
|
||||
<key>url,accountName</key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCombinationTitle</key>
|
||||
<string>Add ${url} to ${accountName}</string>
|
||||
<key>INIntentParameterCombinationTitleID</key>
|
||||
<string>kaKsEY</string>
|
||||
<key>INIntentParameterCombinationUpdatesLinked</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>url,accountName,folderName</key>
|
||||
<key>url,accountName,folderName,title</key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
@@ -57,12 +46,25 @@
|
||||
<key>INIntentParameterCombinationUpdatesLinked</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>url,accountName,title</key>
|
||||
<dict>
|
||||
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCombinationTitle</key>
|
||||
<string>Add ${url} to ${accountName}</string>
|
||||
<key>INIntentParameterCombinationTitleID</key>
|
||||
<string>kaKsEY</string>
|
||||
<key>INIntentParameterCombinationUpdatesLinked</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>INIntentName</key>
|
||||
<string>AddWebFeed</string>
|
||||
<key>INIntentParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
<string>URL</string>
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
@@ -105,6 +107,52 @@
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
<string>Title</string>
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>Ac5RHN</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>2</integer>
|
||||
<key>INIntentParameterMetadata</key>
|
||||
<dict>
|
||||
<key>INIntentParameterMetadataCapitalization</key>
|
||||
<string>Words</string>
|
||||
<key>INIntentParameterMetadataDefaultValueID</key>
|
||||
<string>SVcvQb</string>
|
||||
</dict>
|
||||
<key>INIntentParameterName</key>
|
||||
<string>title</string>
|
||||
<key>INIntentParameterPromptDialogs</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Configuration</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>What is the ${title}of the feed?</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>IGNcSh</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Primary</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentParameterSupportsResolution</key>
|
||||
<true/>
|
||||
<key>INIntentParameterTag</key>
|
||||
<integer>5</integer>
|
||||
<key>INIntentParameterType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCustomDisambiguation</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
@@ -112,7 +160,7 @@
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>CSrgUY</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>2</integer>
|
||||
<integer>3</integer>
|
||||
<key>INIntentParameterMetadata</key>
|
||||
<dict>
|
||||
<key>INIntentParameterMetadataCapitalization</key>
|
||||
@@ -138,14 +186,6 @@
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationIntroduction</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Which one?</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>fWs3li</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationSelection</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
@@ -190,6 +230,8 @@
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterConfigurable</key>
|
||||
<true/>
|
||||
<key>INIntentParameterCustomDisambiguation</key>
|
||||
<true/>
|
||||
<key>INIntentParameterDisplayName</key>
|
||||
@@ -197,7 +239,7 @@
|
||||
<key>INIntentParameterDisplayNameID</key>
|
||||
<string>zXhMPF</string>
|
||||
<key>INIntentParameterDisplayPriority</key>
|
||||
<integer>3</integer>
|
||||
<integer>4</integer>
|
||||
<key>INIntentParameterMetadata</key>
|
||||
<dict>
|
||||
<key>INIntentParameterMetadataCapitalization</key>
|
||||
@@ -223,14 +265,6 @@
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationIntroduction</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Which one?</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>gEzXaM</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationSelection</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>OrganizationIdentifier</key>
|
||||
<string>$(ORGANIZATION_IDENTIFIER)</string>
|
||||
<key>AppGroup</key>
|
||||
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||
<key>AppIdentifierPrefix</key>
|
||||
@@ -44,5 +42,7 @@
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
|
||||
</dict>
|
||||
<key>OrganizationIdentifier</key>
|
||||
<string>$(ORGANIZATION_IDENTIFIER)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -23,7 +23,7 @@ class MasterFeedTableViewCell : VibrantTableViewCell {
|
||||
set {}
|
||||
get {
|
||||
if unreadCount > 0 {
|
||||
let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity")
|
||||
let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessibility")
|
||||
return "\(name) \(unreadCount) \(unreadLabel)"
|
||||
} else {
|
||||
return name
|
||||
|
||||
@@ -26,7 +26,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
let refreshProgressModel = RefreshProgressModel()
|
||||
lazy var progressBarViewController = UIHostingController(rootView: RefreshProgressView(progressBarMode: refreshProgressModel))
|
||||
|
||||
var mainControllerIdentifer = MainControllerIdentifier.masterFeed
|
||||
var mainControllerIdentifier = MainControllerIdentifier.masterFeed
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
@@ -94,6 +94,16 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if (isBeingPresented || isMovingToParent) {
|
||||
// Only show the Twitter alert the first time
|
||||
// the view is presented.
|
||||
presentTwitterDeprecationAlertIfRequired()
|
||||
}
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory {
|
||||
@@ -617,8 +627,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
Context Menu Order:
|
||||
1. Add Web Feed
|
||||
2. Add Reddit Feed
|
||||
3. Add Twitter Feed
|
||||
4. Add Folder
|
||||
3. Add Folder
|
||||
*/
|
||||
|
||||
var menuItems: [UIAction] = []
|
||||
@@ -637,13 +646,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
}
|
||||
menuItems.append(addRedditFeedAction)
|
||||
}
|
||||
if ExtensionPointManager.shared.isTwitterEnabled {
|
||||
let addTwitterFeedActionTitle = NSLocalizedString("Add Twitter Feed", comment: "Add Twitter Feed")
|
||||
let addTwitterFeedAction = UIAction(title: addTwitterFeedActionTitle, image: AppAssets.contextMenuTwitter.tinted(color: .label)) { _ in
|
||||
self.coordinator.showAddTwitterFeed()
|
||||
}
|
||||
menuItems.append(addTwitterFeedAction)
|
||||
}
|
||||
}
|
||||
|
||||
let addWebFolderActionTitle = NSLocalizedString("Add Folder", comment: "Add Folder")
|
||||
@@ -657,7 +659,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
|
||||
self.addNewItemButton.menu = contextMenu
|
||||
}
|
||||
|
||||
|
||||
func focus() {
|
||||
becomeFirstResponder()
|
||||
}
|
||||
@@ -670,6 +672,29 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
||||
}
|
||||
}
|
||||
|
||||
private func presentTwitterDeprecationAlertIfRequired() {
|
||||
if AppDefaults.shared.twitterDeprecationAlertShown { return }
|
||||
|
||||
let expiryDate = Date(timeIntervalSince1970: 1691539200) // August 9th 2023, 00:00 UTC
|
||||
let currentDate = Date()
|
||||
if currentDate > expiryDate {
|
||||
return // If after August 9th, don't show
|
||||
}
|
||||
|
||||
if AccountManager.shared.anyLocalOriCloudAccountHasAtLeastOneTwitterFeed() {
|
||||
showTwitterDeprecationAlert()
|
||||
}
|
||||
AppDefaults.shared.twitterDeprecationAlertShown = true
|
||||
}
|
||||
|
||||
private func showTwitterDeprecationAlert() {
|
||||
let alert = UIAlertController(title: NSLocalizedString("Twitter Integration Removed", comment: "Twitter Integration Removed"),
|
||||
message: NSLocalizedString("On February 1, 2023, Twitter announced the end of free access to the Twitter API, effective February 9.\n\nSince Twitter does not provide RSS feeds, we’ve had to use the Twitter API. Without free access to that API, we can’t read feeds from Twitter.\n\nWe’ve left your Twitter feeds intact. If you have any starred items from those feeds, they will remain as long as you don’t delete those feeds.\n\nYou can still read whatever you have already downloaded. However, those feeds will no longer update.", comment: "Twitter deprecation message"),
|
||||
preferredStyle: .alert)
|
||||
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel))
|
||||
present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIContextMenuInteractionDelegate
|
||||
@@ -693,6 +718,9 @@ extension MasterFeedViewController: UIContextMenuInteractionDelegate {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [markAllAction]))
|
||||
}
|
||||
|
||||
if let catchUpAction = self.catchUpActionMenu(account: account, contentView: interaction.view) {
|
||||
menuElements.append(catchUpAction)
|
||||
}
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [self.deactivateAccountAction(account: account)]))
|
||||
|
||||
return UIMenu(title: "", children: menuElements)
|
||||
@@ -921,6 +949,11 @@ private extension MasterFeedViewController {
|
||||
|
||||
if let markAllAction = self.markAllAsReadAction(indexPath: indexPath) {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [markAllAction]))
|
||||
|
||||
}
|
||||
|
||||
if let catchUpAction = self.catchUpActionMenu(indexPath: indexPath) {
|
||||
menuElements.append(catchUpAction)
|
||||
}
|
||||
|
||||
if includeDeleteRename {
|
||||
@@ -948,6 +981,10 @@ private extension MasterFeedViewController {
|
||||
if let markAllAction = self.markAllAsReadAction(indexPath: indexPath) {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [markAllAction]))
|
||||
}
|
||||
|
||||
if let catchUpAction = self.catchUpActionMenu(indexPath: indexPath) {
|
||||
menuElements.append(catchUpAction)
|
||||
}
|
||||
|
||||
menuElements.append(UIMenu(title: "",
|
||||
options: .displayInline,
|
||||
@@ -961,13 +998,22 @@ private extension MasterFeedViewController {
|
||||
})
|
||||
}
|
||||
|
||||
func makePseudoFeedContextMenu(indexPath: IndexPath) -> UIContextMenuConfiguration? {
|
||||
guard let markAllAction = self.markAllAsReadAction(indexPath: indexPath) else {
|
||||
return nil
|
||||
}
|
||||
func makePseudoFeedContextMenu(indexPath: IndexPath) -> UIContextMenuConfiguration {
|
||||
return UIContextMenuConfiguration(identifier: MasterFeedRowIdentifier(indexPath: indexPath), previewProvider: nil, actionProvider: { [weak self] suggestedActions in
|
||||
|
||||
return UIContextMenuConfiguration(identifier: MasterFeedRowIdentifier(indexPath: indexPath), previewProvider: nil, actionProvider: { suggestedActions in
|
||||
return UIMenu(title: "", children: [markAllAction])
|
||||
guard let self = self else { return nil }
|
||||
|
||||
var menuElements = [UIMenuElement]()
|
||||
|
||||
if let markAllAction = self.markAllAsReadAction(indexPath: indexPath) {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [markAllAction]))
|
||||
}
|
||||
|
||||
if let catchUpAction = self.catchUpActionMenu(indexPath: indexPath) {
|
||||
menuElements.append(catchUpAction)
|
||||
}
|
||||
|
||||
return UIMenu(title: "", children: menuElements)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1151,6 +1197,97 @@ private extension MasterFeedViewController {
|
||||
return action
|
||||
}
|
||||
|
||||
func catchUpActionMenu(indexPath: IndexPath) -> UIMenu? {
|
||||
guard let feed = coordinator.nodeFor(indexPath)?.representedObject as? Feed,
|
||||
let contentView = self.tableView.cellForRow(at: indexPath)?.contentView,
|
||||
feed.unreadCount > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Doesn't make sense to mark articles newer than a day with catch up with first option being older than a day
|
||||
if let maybeSmartFeed = feed as? SmartFeed {
|
||||
if maybeSmartFeed.delegate is TodayFeedDelegate {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let title = NSLocalizedString("Mark as Read Older Than", comment: "Command")
|
||||
let oneDayAction = UIAction(title: "1 Day") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Day as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -1, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let twoDayAction = UIAction(title: "2 Days") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 2 Days as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -2, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let threeDayAction = UIAction(title: "3 Days") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 3 Days as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -3, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneWeekAction = UIAction(title: "1 Week") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Week as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .weekOfYear, value: -1, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let twoWeekAction = UIAction(title: "2 Weeks") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 2 Weeks as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .weekOfYear, value: -2, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneMonthAction = UIAction(title: "1 Month") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Month as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .month, value: -1, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneYearAction = UIAction(title: "1 Year") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Year as Read", sourceType: contentView) { [weak self] in
|
||||
let cutoff = Calendar.current.date(byAdding: .year, value: -1, to: Date())
|
||||
if let articles = try? feed.fetchUnreadArticlesBetween(before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
var markActions = [UIAction]()
|
||||
markActions.append(oneDayAction)
|
||||
markActions.append(twoDayAction)
|
||||
markActions.append(threeDayAction)
|
||||
markActions.append(oneWeekAction)
|
||||
markActions.append(twoWeekAction)
|
||||
markActions.append(oneMonthAction)
|
||||
markActions.append(oneYearAction)
|
||||
let majorMenu = UIMenu(title: title, image: getMarkOlderImageDirection(), children: markActions)
|
||||
|
||||
return majorMenu
|
||||
}
|
||||
|
||||
func getMarkOlderImageDirection() -> UIImage {
|
||||
if AppDefaults.shared.timelineSortDirection == .orderedDescending {
|
||||
return AppAssets.markBelowAsReadImage
|
||||
} else {
|
||||
return AppAssets.markAboveAsReadImage
|
||||
}
|
||||
}
|
||||
func markAllAsReadAction(account: Account, contentView: UIView?) -> UIAction? {
|
||||
guard account.unreadCount > 0, let contentView = contentView else {
|
||||
return nil
|
||||
@@ -1171,6 +1308,102 @@ private extension MasterFeedViewController {
|
||||
return action
|
||||
}
|
||||
|
||||
func catchUpActionMenu(account: Account, contentView: UIView?) -> UIMenu? {
|
||||
guard account.unreadCount > 0, let contentView = contentView else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let title = NSLocalizedString("Mark as Read Older Than", comment: "Command")
|
||||
let oneDayAction = UIAction(title: "1 Day") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Day as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -1, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let twoDayAction = UIAction(title: "2 Days") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 2 Days as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -2, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let threeDayAction = UIAction(title: "3 Days") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 3 Days as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .day, value: -3, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneWeekAction = UIAction(title: "1 Week") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Week as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .weekOfYear, value: -1, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let twoWeekAction = UIAction(title: "2 Weeks") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 2 Weeks as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .weekOfYear, value: -2, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneMonthAction = UIAction(title: "1 Month") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Month as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .month, value: -1, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let oneYearAction = UIAction(title: "1 Year") { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: "Mark Older Than 1 Year as Read", sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
let cutoff = Calendar.current.date(byAdding: .year, value: -1, to: Date())
|
||||
if let articles = try? account.fetchUnreadArticlesBetween(limit: nil, before: cutoff, after: nil) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var markActions = [UIAction]()
|
||||
markActions.append(oneDayAction)
|
||||
markActions.append(twoDayAction)
|
||||
markActions.append(threeDayAction)
|
||||
markActions.append(oneWeekAction)
|
||||
markActions.append(twoWeekAction)
|
||||
markActions.append(oneMonthAction)
|
||||
markActions.append(oneYearAction)
|
||||
let majorMenu = UIMenu(title: title, image: getMarkOlderImageDirection(), children: markActions)
|
||||
|
||||
return majorMenu
|
||||
}
|
||||
|
||||
|
||||
func rename(indexPath: IndexPath) {
|
||||
guard let feed = coordinator.nodeFor(indexPath)?.representedObject as? Feed else { return }
|
||||
|
||||
@@ -22,7 +22,7 @@ class MasterTimelineTitleView: UIView {
|
||||
set { }
|
||||
get {
|
||||
if let name = label.text {
|
||||
let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity")
|
||||
let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessibility")
|
||||
return "\(name) \(unreadCountView.unreadCount) \(unreadLabel)"
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -31,7 +31,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
private lazy var dataSource = makeDataSource()
|
||||
private let searchController = UISearchController(searchResultsController: nil)
|
||||
|
||||
var mainControllerIdentifer = MainControllerIdentifier.masterTimeline
|
||||
var mainControllerIdentifier = MainControllerIdentifier.masterTimeline
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 969 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 689 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "twitterContextMenu.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "twitter.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "twitter_white.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.9 KiB |
@@ -6,14 +6,33 @@
|
||||
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||
<key>AppIdentifierPrefix</key>
|
||||
<string>$(AppIdentifierPrefix)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.ranchero.NetNewsWire.FeedRefresh</string>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>nnwtheme</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>NetNewsWire Theme</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.ranchero.netnewswire.theme</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -54,6 +73,8 @@
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>DeveloperEntitlements</key>
|
||||
<string>$(DEVELOPER_ENTITLEMENTS)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>mailto</string>
|
||||
@@ -61,6 +82,8 @@
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
@@ -195,29 +218,6 @@
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>nnwtheme</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>NetNewsWire Theme</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.ranchero.netnewswire.theme</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>UserAgent</key>
|
||||
<string>NetNewsWire (RSS Reader; https://netnewswire.com/)</string>
|
||||
</dict>
|
||||
|
||||
@@ -16,7 +16,7 @@ import SafariServices
|
||||
import SwiftUI
|
||||
|
||||
protocol MainControllerIdentifiable {
|
||||
var mainControllerIdentifer: MainControllerIdentifier { get }
|
||||
var mainControllerIdentifier: MainControllerIdentifier { get }
|
||||
}
|
||||
|
||||
enum MainControllerIdentifier {
|
||||
@@ -352,6 +352,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let isSidebarHidden = windowState[UserInfoKey.isSidebarHidden] as? Bool, isSidebarHidden {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
self.rootSplitViewController.preferredDisplayMode = .secondaryOnly
|
||||
}
|
||||
}
|
||||
|
||||
rebuildBackingStores(initialLoad: true)
|
||||
|
||||
@@ -967,7 +973,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
func selectPrevUnread() {
|
||||
|
||||
// This should never happen, but I don't want to risk throwing us
|
||||
// into an infinate loop searching for an unread that isn't there.
|
||||
// into an infinite loop searching for an unread that isn't there.
|
||||
if appDelegate.unreadCount < 1 {
|
||||
return
|
||||
}
|
||||
@@ -988,7 +994,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
func selectNextUnread() {
|
||||
|
||||
// This should never happen, but I don't want to risk throwing us
|
||||
// into an infinate loop searching for an unread that isn't there.
|
||||
// into an infinite loop searching for an unread that isn't there.
|
||||
if appDelegate.unreadCount < 1 {
|
||||
return
|
||||
}
|
||||
@@ -1210,13 +1216,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
masterFeedViewController.present(addNavViewController, animated: true)
|
||||
}
|
||||
|
||||
func showAddTwitterFeed() {
|
||||
let addNavViewController = UIStoryboard.twitterAdd.instantiateInitialViewController() as! UINavigationController
|
||||
addNavViewController.modalPresentationStyle = .formSheet
|
||||
addNavViewController.preferredContentSize = AddFeedViewController.preferredContentSizeForFormSheetDisplay
|
||||
masterFeedViewController.present(addNavViewController, animated: true)
|
||||
}
|
||||
|
||||
func showAddFolder() {
|
||||
let addNavViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddFolderViewControllerNav") as! UINavigationController
|
||||
addNavViewController.modalPresentationStyle = .formSheet
|
||||
@@ -1374,9 +1373,9 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
||||
|
||||
defer {
|
||||
if let mainController = viewController as? MainControllerIdentifiable {
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifer
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifier
|
||||
} else if let mainController = (viewController as? UINavigationController)?.topViewController as? MainControllerIdentifiable {
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifer
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2124,7 +2123,8 @@ private extension SceneCoordinator {
|
||||
return [
|
||||
UserInfoKey.readFeedsFilterState: isReadFeedsFiltered,
|
||||
UserInfoKey.containerExpandedWindowState: containerExpandedWindowState,
|
||||
UserInfoKey.readArticlesFilterState: readArticlesFilterState
|
||||
UserInfoKey.readArticlesFilterState: readArticlesFilterState,
|
||||
UserInfoKey.isSidebarHidden: rootSplitViewController.displayMode == .secondaryOnly
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,6 @@ extension UIStoryboard: Logging {
|
||||
return UIStoryboard(name: "RedditAdd", bundle: nil)
|
||||
}
|
||||
|
||||
static var twitterAdd: UIStoryboard {
|
||||
return UIStoryboard(name: "TwitterAdd", bundle: nil)
|
||||
}
|
||||
|
||||
static var settings: UIStoryboard {
|
||||
return UIStoryboard(name: "Settings", bundle: nil)
|
||||
}
|
||||
|
||||