Combined all the "add"s into a single popover.

This commit is contained in:
Maurice Parker
2019-04-16 13:38:07 -05:00
parent de890189cf
commit 615e183242
12 changed files with 352 additions and 106 deletions

428
iOS/Add/Add.storyboard Normal file
View File

@@ -0,0 +1,428 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bqE-WD-JXz">
<device id="retina5_9" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Add Feed-->
<scene sceneID="2Tc-JN-edX">
<objects>
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="3tl-Mb-Eno">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="lyJ-rf-8GA">
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lyJ-rf-8GA" id="eNS-Rp-w0A">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="URL" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eRp-AP-WFq">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="7aE-6a-iP7" id="zCK-Sy-4Zr"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="eRp-AP-WFq" firstAttribute="top" secondItem="eNS-Rp-w0A" secondAttribute="top" constant="4" id="80p-a2-3NC"/>
<constraint firstItem="eRp-AP-WFq" firstAttribute="leading" secondItem="eNS-Rp-w0A" secondAttribute="leading" constant="20" symbolic="YES" id="bHJ-7l-Pl3"/>
<constraint firstAttribute="bottom" secondItem="eRp-AP-WFq" secondAttribute="bottom" constant="4" id="fs0-iw-zTo"/>
<constraint firstAttribute="trailing" secondItem="eRp-AP-WFq" secondAttribute="trailing" constant="20" symbolic="YES" id="xWD-54-Kdm"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="Pxz-fv-QhQ">
<rect key="frame" x="0.0" y="79" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Pxz-fv-QhQ" id="8aP-2A-8jc">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Title (Optional)" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="u7n-VL-Ho9">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="u7n-VL-Ho9" secondAttribute="bottom" constant="4" id="CdB-LH-PJT"/>
<constraint firstItem="u7n-VL-Ho9" firstAttribute="leading" secondItem="8aP-2A-8jc" secondAttribute="leading" constant="20" symbolic="YES" id="RML-Iw-gsd"/>
<constraint firstItem="u7n-VL-Ho9" firstAttribute="top" secondItem="8aP-2A-8jc" secondAttribute="top" constant="4" id="dxi-LX-hFa"/>
<constraint firstAttribute="trailing" secondItem="u7n-VL-Ho9" secondAttribute="trailing" constant="20" symbolic="YES" id="kQl-v5-eVa"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="qn9-7O-LoA">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="MGg-y2-M2D">
<rect key="frame" x="0.0" y="129" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MGg-y2-M2D" id="sZh-wI-IW4">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<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">
<rect key="frame" x="23.999999999999996" y="15" width="48.666666666666657" height="14"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</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">
<rect key="frame" x="325" y="15" width="42" height="14"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="vaV-kY-CaE" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="grZ-g6-qfm" secondAttribute="trailing" constant="8" id="1Dk-MH-7Hw"/>
<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="leadingMargin" constant="8" id="NKz-GB-i4E"/>
<constraint firstAttribute="bottomMargin" secondItem="vaV-kY-CaE" secondAttribute="bottom" constant="4" id="Yfx-6j-UqX"/>
<constraint firstItem="vaV-kY-CaE" firstAttribute="trailing" secondItem="sZh-wI-IW4" secondAttribute="trailingMargin" constant="8" id="eCs-ob-UXo"/>
<constraint firstItem="vaV-kY-CaE" firstAttribute="top" secondItem="sZh-wI-IW4" secondAttribute="topMargin" constant="4" id="hUO-Ln-i29"/>
<constraint firstAttribute="bottomMargin" secondItem="grZ-g6-qfm" secondAttribute="bottom" constant="4" id="tu3-aF-XD6"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="200" id="PiN-2i-6Dj">
<rect key="frame" x="0.0" y="173" width="375" height="200"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="199.66666666666666"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<pickerView contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="v2n-nX-8jq">
<rect key="frame" x="0.0" y="0.0" width="375" height="170"/>
</pickerView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="v2n-nX-8jq" secondAttribute="trailing" id="IBi-SI-10J"/>
<constraint firstItem="v2n-nX-8jq" firstAttribute="top" secondItem="sZ4-hj-gua" secondAttribute="top" id="kmm-i3-6DB"/>
<constraint firstItem="v2n-nX-8jq" firstAttribute="leading" secondItem="sZ4-hj-gua" secondAttribute="leading" id="ksr-vY-KdS"/>
<constraint firstAttribute="bottom" secondItem="v2n-nX-8jq" secondAttribute="bottom" id="wf0-0Y-GNZ"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="7aE-6a-iP7" id="PAe-eu-KhR"/>
<outlet property="delegate" destination="7aE-6a-iP7" id="zYS-q2-iEf"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Add Feed" id="i1W-2z-PAk">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="vdU-kc-SkI">
<connections>
<action selector="cancel:" destination="7aE-6a-iP7" id="v9C-5Y-7Pf"/>
</connections>
</barButtonItem>
<rightBarButtonItems>
<barButtonItem enabled="NO" title="Add" id="M4A-Uu-rC9">
<connections>
<action selector="add:" destination="7aE-6a-iP7" id="WZ4-RF-9k2"/>
</connections>
</barButtonItem>
<barButtonItem style="plain" id="r7V-oB-aHz">
<view key="customView" contentMode="scaleToFill" id="4in-Eb-Rxp">
<rect key="frame" x="335" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="3ZH-9O-T3i">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="activityIndicatorView" destination="3ZH-9O-T3i" id="Z7G-Qb-tV9"/>
<outlet property="addButton" destination="M4A-Uu-rC9" id="HXl-Sg-Zgw"/>
<outlet property="cancelButton" destination="vdU-kc-SkI" id="AKF-i4-V5Q"/>
<outlet property="folderLabel" destination="vaV-kY-CaE" id="xeO-Ks-LIy"/>
<outlet property="folderPickerView" destination="v2n-nX-8jq" id="qwz-Gg-GdQ"/>
<outlet property="nameTextField" destination="u7n-VL-Ho9" id="YQV-Xq-f9q"/>
<outlet property="urlTextField" destination="eRp-AP-WFq" id="FG3-pH-2Fh"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TO9-rb-MQ7" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1711.2" y="89.408866995073893"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="9m0-Wv-Ipq">
<objects>
<navigationController storyboardIdentifier="AddContainerNavigationViewController" id="bqE-WD-JXz" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="dZt-u5-9kz">
<rect key="frame" x="0.0" y="44" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="G8j-dh-7Ji" kind="relationship" relationship="rootViewController" id="rGX-Hf-QYs"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ay4-WY-2PI" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3233" y="91"/>
</scene>
<!--Add Container View Controller-->
<scene sceneID="ICN-hJ-Mn2">
<objects>
<viewController storyboardIdentifier="AddContainerViewController" id="G8j-dh-7Ji" customClass="AddContainerViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="cJo-ve-mLk">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="1Ce-E7-rG2">
<rect key="frame" x="90.666666666666686" y="108" width="194" height="29"/>
<segments>
<segment title="Feed"/>
<segment title="Folder"/>
<segment title="Account"/>
</segments>
<connections>
<action selector="typeSelectorChanged:" destination="G8j-dh-7Ji" eventType="valueChanged" id="evz-dY-V8G"/>
</connections>
</segmentedControl>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TUN-mZ-uLE">
<rect key="frame" x="0.0" y="156" width="375" height="656"/>
</containerView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="TUN-mZ-uLE" secondAttribute="bottom" id="GrY-qn-cjm"/>
<constraint firstItem="TUN-mZ-uLE" firstAttribute="trailing" secondItem="pP4-52-FAl" secondAttribute="trailing" id="SJI-cb-6hr"/>
<constraint firstItem="1Ce-E7-rG2" firstAttribute="top" secondItem="pP4-52-FAl" secondAttribute="top" constant="20" id="UfR-Fy-drx"/>
<constraint firstItem="TUN-mZ-uLE" firstAttribute="top" secondItem="1Ce-E7-rG2" secondAttribute="bottom" constant="20" id="a7e-Vk-UVo"/>
<constraint firstItem="TUN-mZ-uLE" firstAttribute="leading" secondItem="pP4-52-FAl" secondAttribute="leading" id="ab5-qk-Xmn"/>
<constraint firstItem="1Ce-E7-rG2" firstAttribute="centerX" secondItem="cJo-ve-mLk" secondAttribute="centerX" id="uAg-Xx-OkN"/>
</constraints>
<viewLayoutGuide key="safeArea" id="pP4-52-FAl"/>
</view>
<navigationItem key="navigationItem" id="3Al-Zd-2cS">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="Uz9-n0-t2M">
<connections>
<action selector="cancel:" destination="G8j-dh-7Ji" id="7m6-ad-a2V"/>
</connections>
</barButtonItem>
<rightBarButtonItems>
<barButtonItem title="Add" id="tVq-rz-5pe">
<connections>
<action selector="add:" destination="G8j-dh-7Ji" id="fWb-l7-nc0"/>
</connections>
</barButtonItem>
<barButtonItem style="plain" id="chA-NW-oNo">
<view key="customView" contentMode="scaleToFill" id="nG1-ZO-oLQ">
<rect key="frame" x="300" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Vwh-O4-dFl">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="activityIndicatorView" destination="Vwh-O4-dFl" id="xJ0-0X-Yq0"/>
<outlet property="addButton" destination="tVq-rz-5pe" id="kNP-KC-naP"/>
<outlet property="cancelButton" destination="Uz9-n0-t2M" id="dZu-nz-p4J"/>
<outlet property="containerView" destination="TUN-mZ-uLE" id="8p5-N0-LRa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="KrE-EC-ioA" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2452" y="90"/>
</scene>
<!--Add Folder-->
<scene sceneID="m7L-uI-ghq">
<objects>
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="12M-tp-EeV">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="XJI-0M-cAh">
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XJI-0M-cAh" id="tIS-Tx-i17">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="lZK-wx-jbo">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="3dI-34-ljo" id="rrs-yw-joz"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="lZK-wx-jbo" firstAttribute="leading" secondItem="tIS-Tx-i17" secondAttribute="leading" constant="20" symbolic="YES" id="62N-uM-0fD"/>
<constraint firstAttribute="trailing" secondItem="lZK-wx-jbo" secondAttribute="trailing" constant="20" symbolic="YES" id="6b0-Xh-58P"/>
<constraint firstAttribute="bottom" secondItem="lZK-wx-jbo" secondAttribute="bottom" constant="4" id="QRV-tV-NB8"/>
<constraint firstItem="lZK-wx-jbo" firstAttribute="top" secondItem="tIS-Tx-i17" secondAttribute="top" constant="4" id="dli-qZ-hdA"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="bX9-Y2-S2D">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="uU0-Jh-goT">
<rect key="frame" x="0.0" y="85" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uU0-Jh-goT" id="y2g-dW-fPZ">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<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">
<rect key="frame" x="20" y="4" width="64" height="35.666666666666664"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</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">
<rect key="frame" x="325" y="15" width="42" height="14"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<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 firstAttribute="bottomMargin" secondItem="mxj-Bw-Jfx" secondAttribute="bottom" constant="4" id="KY1-7G-Jh8"/>
<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="YRf-I7-nkL" firstAttribute="leading" secondItem="y2g-dW-fPZ" secondAttribute="leading" constant="20" symbolic="YES" id="xBK-tz-P2j"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="159" id="zRi-p6-4KU">
<rect key="frame" x="0.0" y="129" width="375" height="159"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="158.66666666666666"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<pickerView contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eGY-V8-gzJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="170"/>
</pickerView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="eGY-V8-gzJ" secondAttribute="trailing" id="Ghf-jL-Xrr"/>
<constraint firstItem="eGY-V8-gzJ" firstAttribute="top" secondItem="wek-Qh-OXr" secondAttribute="top" id="Ldh-IT-4bH"/>
<constraint firstItem="eGY-V8-gzJ" firstAttribute="leading" secondItem="wek-Qh-OXr" secondAttribute="leading" id="cTY-21-rTK"/>
<constraint firstAttribute="bottom" secondItem="eGY-V8-gzJ" secondAttribute="bottom" id="h5X-Sg-5mV"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="3dI-34-ljo" id="Bz5-33-FXs"/>
<outlet property="delegate" destination="3dI-34-ljo" id="ZXX-gI-6ii"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Add Folder" id="LuA-AC-n99">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="v7P-5Z-FqV">
<connections>
<action selector="cancel:" destination="3dI-34-ljo" id="FA8-9i-9CA"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" enabled="NO" title="Add" id="dgF-XT-J4d">
<connections>
<action selector="add:" destination="3dI-34-ljo" id="BIP-Db-2K5"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="accountLabel" destination="mxj-Bw-Jfx" id="l4A-ta-dUi"/>
<outlet property="accountPickerView" destination="eGY-V8-gzJ" id="zGk-k4-Jnd"/>
<outlet property="addButton" destination="dgF-XT-J4d" id="Zu1-fQ-xdl"/>
<outlet property="nameTextField" destination="lZK-wx-jbo" id="Xvq-LH-kwC"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="yp4-mF-DZV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-954.39999999999998" y="89.408866995073893"/>
</scene>
<!--Add Account View Controller-->
<scene sceneID="Wy0-Z6-lrg">
<objects>
<tableViewController storyboardIdentifier="AddAccountViewController" id="L9h-bF-xeh" customClass="AddAccountViewController" 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="YtA-U7-Ox4">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="D3p-uk-AI8">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="jst-2Y-7Br">
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jst-2Y-7Br" id="j6d-MU-b7V">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Coming soon..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6AT-sI-3DG">
<rect key="frame" x="130" y="11" width="115" height="22"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="6AT-sI-3DG" firstAttribute="top" secondItem="j6d-MU-b7V" secondAttribute="topMargin" id="AyG-c8-kOT"/>
<constraint firstItem="6AT-sI-3DG" firstAttribute="centerX" secondItem="j6d-MU-b7V" secondAttribute="centerX" id="Klf-co-7i2"/>
<constraint firstItem="6AT-sI-3DG" firstAttribute="bottom" secondItem="j6d-MU-b7V" secondAttribute="bottomMargin" id="UOG-ny-eea"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="L9h-bF-xeh" id="9j2-Wv-4xJ"/>
<outlet property="delegate" destination="L9h-bF-xeh" id="6fV-zk-qu3"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="aqp-ki-yLv" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-210" y="87"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,21 @@
//
// AddAccountViewController.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/16/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import UIKit
class AddAccountViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 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)
}
}

View File

@@ -0,0 +1,101 @@
//
// AddContainerViewController.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/16/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import UIKit
class AddContainerViewController: UIViewController {
@IBOutlet weak var cancelButton: UIBarButtonItem!
@IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
@IBOutlet weak var addButton: UIBarButtonItem!
@IBOutlet weak var containerView: UIView!
private var currentViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
activityIndicatorView.isHidden = true
switchToFeed()
}
@IBAction func typeSelectorChanged(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
switchToFeed()
case 1:
switchToFolder()
default:
switchToAccount()
}
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true)
}
@IBAction func add(_ sender: Any) {
}
}
private extension AddContainerViewController {
func switchToFeed() {
guard !(currentViewController is AddFeedViewController) else {
return
}
hideCurrentController()
displayContentController(UIStoryboard.add.instantiateController(ofType: AddFeedViewController.self))
}
func switchToFolder() {
guard !(currentViewController is AddFolderViewController) else {
return
}
hideCurrentController()
displayContentController(UIStoryboard.add.instantiateController(ofType: AddFolderViewController.self))
}
func switchToAccount() {
guard !(currentViewController is AddAccountViewController) else {
return
}
hideCurrentController()
displayContentController(UIStoryboard.add.instantiateController(ofType: AddAccountViewController.self))
}
func displayContentController(_ controller: UIViewController) {
addChild(controller)
containerView.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false
controller.view.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
controller.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
controller.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
controller.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
controller.didMove(toParent: self)
}
func hideCurrentController() {
guard let currentViewController = currentViewController else {
return
}
currentViewController.willMove(toParent: nil)
currentViewController.view.removeFromSuperview()
currentViewController.removeFromParent()
}
}

View File

@@ -0,0 +1,47 @@
//
// AddFeedFolderPickerData.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/16/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import Account
import RSCore
import RSTree
struct AddFeedFolderPickerData {
var containerNames = [String]()
var containers = [Container]()
init() {
let treeControllerDelegate = FolderTreeControllerDelegate()
let rootNode = Node(representedObject: AccountManager.shared.localAccount, parent: nil)
rootNode.canHaveChildNodes = true
let treeController = TreeController(delegate: treeControllerDelegate, rootNode: rootNode)
guard let rootNameProvider = treeController.rootNode.representedObject as? DisplayNameProvider else {
return
}
let rootName = rootNameProvider.nameForDisplay
containerNames.append(rootName)
containers.append(treeController.rootNode.representedObject as! Container)
treeController.rootNode.childNodes.forEach { node in
guard let childContainer = node.representedObject as? Container else {
return
}
let childName = (childContainer as! DisplayNameProvider).nameForDisplay
containerNames.append("\(rootName) / \(childName)")
containers.append(childContainer)
}
}
}

View File

@@ -0,0 +1,258 @@
//
// AddFeedViewController.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/16/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import UIKit
import Account
import RSCore
import RSTree
import RSParser
class AddFeedViewController: UITableViewController {
@IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
@IBOutlet weak var cancelButton: UIBarButtonItem!
@IBOutlet weak var addButton: UIBarButtonItem!
@IBOutlet weak var urlTextField: UITextField!
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var folderPickerView: UIPickerView!
@IBOutlet weak var folderLabel: UILabel!
private var pickerData: AddFeedFolderPickerData!
private var feedFinder: FeedFinder?
private var userEnteredURL: URL?
private var userEnteredFolder: Folder?
private var userEnteredTitle: String?
private var userEnteredAccount: Account?
private var foundFeedURLString: String?
private var bestFeedSpecifier: FeedSpecifier?
private var titleFromFeed: String?
private var userCancelled = false
override func viewDidLoad() {
super.viewDidLoad()
activityIndicatorView.isHidden = true
urlTextField.autocorrectionType = .no
urlTextField.autocapitalizationType = .none
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.
tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0)
}
@IBAction func cancel(_ sender: Any) {
userCancelled = true
dismiss(animated: true)
}
@IBAction func add(_ sender: Any) {
let urlString = urlTextField.text ?? ""
let normalizedURLString = (urlString as NSString).rs_normalizedURL()
guard !normalizedURLString.isEmpty, let url = URL(string: normalizedURLString) else {
dismiss(animated: true)
return
}
userEnteredURL = url
userEnteredTitle = nameTextField.text
let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)]
if let account = container as? Account {
userEnteredAccount = account
}
if let folder = container as? Folder, let account = folder.account {
userEnteredAccount = account
userEnteredFolder = folder
}
guard let userEnteredAccount = userEnteredAccount else {
assertionFailure()
return
}
if userEnteredAccount.hasFeed(withURL: url.absoluteString) {
showAlreadySubscribedError()
return
}
beginShowingProgress()
feedFinder = FeedFinder(url: url, delegate: self)
}
}
extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate {
func numberOfComponents(in pickerView: UIPickerView) ->Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.containerNames.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData.containerNames[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
folderLabel.text = pickerData.containerNames[row]
}
}
extension AddFeedViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
updateUI()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
updateUI()
}
}
extension AddFeedViewController: FeedFinderDelegate {
public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {
if userCancelled {
endShowingProgress()
return
}
if let error = feedFinder.initialDownloadError {
if feedFinder.initialDownloadStatusCode == 404 {
endShowingProgress()
showNoFeedsErrorMessage()
} else {
endShowingProgress()
showInitialDownloadError(error)
}
return
}
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
endShowingProgress()
showNoFeedsErrorMessage()
return
}
self.bestFeedSpecifier = bestFeedSpecifier
self.foundFeedURLString = bestFeedSpecifier.urlString
if let url = URL(string: bestFeedSpecifier.urlString) {
InitialFeedDownloader.download(url) { (parsedFeed) in
self.titleFromFeed = parsedFeed?.title
self.addFeedIfPossible(parsedFeed)
}
} else {
// Shouldn't happen.
endShowingProgress()
showNoFeedsErrorMessage()
}
}
}
private extension AddFeedViewController {
private func updateUI() {
addButton.isEnabled = urlTextField.text?.rs_stringMayBeURL() ?? false
}
private func beginShowingProgress() {
activityIndicatorView.isHidden = false
activityIndicatorView.startAnimating()
addButton.isEnabled = false
}
private func endShowingProgress() {
activityIndicatorView.isHidden = true
activityIndicatorView.stopAnimating()
addButton.isEnabled = true
}
private func showAlreadySubscribedError() {
let title = NSLocalizedString("Already subscribed", comment: "Feed finder")
let message = NSLocalizedString("Cant add this feed because youve already subscribed to it.", comment: "Feed finder")
presentError(title: title, message: message)
}
private func showNoFeedsErrorMessage() {
let title = NSLocalizedString("Feed not found", comment: "Feed finder")
let message = NSLocalizedString("Cant add a feed because no feed was found.", comment: "Feed finder")
presentError(title: title, message: message)
}
private func showInitialDownloadError(_ error: Error) {
let title = NSLocalizedString("Download Error", comment: "Feed finder")
let formatString = NSLocalizedString("Cant add this feed because of a download error: “%@”", comment: "Feed finder")
let message = NSString.localizedStringWithFormat(formatString as NSString, error.localizedDescription)
presentError(title: title, message: message as String)
}
func addFeedIfPossible(_ parsedFeed: ParsedFeed?) {
if userCancelled {
endShowingProgress()
return
}
guard let account = userEnteredAccount else {
assertionFailure("Expected account.")
return
}
guard let feedURLString = foundFeedURLString else {
assertionFailure("Expected feedURLString.")
return
}
if account.hasFeed(withURL: feedURLString) {
endShowingProgress()
showAlreadySubscribedError()
return
}
guard let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) else {
endShowingProgress()
return
}
if let parsedFeed = parsedFeed {
account.update(feed, with: parsedFeed, {})
}
account.addFeed(feed, to: userEnteredFolder)
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
endShowingProgress()
dismiss(animated: true)
}
}

View File

@@ -0,0 +1,92 @@
//
// AddFolderViewController.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/16/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import UIKit
import Account
import RSCore
class AddFolderViewController: UITableViewController {
@IBOutlet weak var addButton: UIBarButtonItem!
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var accountLabel: UILabel!
@IBOutlet weak var accountPickerView: UIPickerView!
private var accounts: [Account]!
override func viewDidLoad() {
super.viewDidLoad()
accounts = AccountManager.shared.sortedAccounts
accountLabel.text = (accounts[0] as DisplayNameProvider).nameForDisplay
accountPickerView.dataSource = self
accountPickerView.delegate = self
// 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)
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true)
}
@IBAction func add(_ sender: Any) {
let account = accounts[accountPickerView.selectedRow(inComponent: 0)]
if let folderName = nameTextField.text {
account.ensureFolder(with: folderName)
}
dismiss(animated: true)
}
}
extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate {
func numberOfComponents(in pickerView: UIPickerView) ->Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return accounts.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return (accounts[row] as DisplayNameProvider).nameForDisplay
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
accountLabel.text = (accounts[row] as DisplayNameProvider).nameForDisplay
}
}
extension AddFolderViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
updateUI()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
updateUI()
}
}
private extension AddFolderViewController {
private func updateUI() {
addButton.isEnabled = !(nameTextField.text?.isEmpty ?? false)
}
}

View File

@@ -0,0 +1,43 @@
//
// FolderTreeControllerDelegate.swift
// NetNewsWire
//
// Created by Brent Simmons on 8/10/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
import RSTree
import Articles
import Account
final class FolderTreeControllerDelegate: TreeControllerDelegate {
func treeController(treeController: TreeController, childNodesFor node: Node) -> [Node]? {
return node.isRoot ? childNodesForRootNode(node) : nil
}
}
private extension FolderTreeControllerDelegate {
func childNodesForRootNode(_ node: Node) -> [Node]? {
// Root node is Top Level and children are folders. Folders cant have subfolders.
// This will have to be revised later.
guard let folders = AccountManager.shared.localAccount.folders else {
return nil
}
let folderNodes = folders.map { createNode($0, parent: node) }
return folderNodes.sortedAlphabetically()
}
func createNode(_ folder: Folder, parent: Node) -> Node {
let node = Node(representedObject: folder, parent: parent)
node.canHaveChildNodes = false
return node
}
}