mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
AddAccount sheets done
This commit is contained in:
@@ -1,810 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Modal Navigation Controller-->
|
||||
<scene sceneID="98f-PW-S1C">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="LocalAccountNavigationViewController" id="TMY-HB-vAu" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="p8g-7e-3f4">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="vi3-jb-8XS" kind="relationship" relationship="rootViewController" id="dIe-7d-ZQX"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="6sV-68-OXu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1880" y="-528"/>
|
||||
</scene>
|
||||
<!--Modal Navigation Controller-->
|
||||
<scene sceneID="6i4-ho-e4F">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="FeedbinAccountNavigationViewController" id="sFg-MZ-PqJ" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="wq6-np-tNn">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="ECy-jg-Kyc" kind="relationship" relationship="rootViewController" id="usT-8C-GGf"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Lfz-4s-0Vn" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3177" y="-528"/>
|
||||
</scene>
|
||||
<!--On My Device-->
|
||||
<scene sceneID="J93-FN-Yey">
|
||||
<objects>
|
||||
<tableViewController id="vi3-jb-8XS" customClass="LocalAccountViewController" 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="YLa-nM-G7t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="Vxr-5V-V6R">
|
||||
<rect key="frame" x="0.0" y="159" width="414" height="150"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Local accounts do not sync your feeds across devices." textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5ce-ZL-glQ">
|
||||
<rect key="frame" x="20" y="8" width="373" height="13.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="5ce-ZL-glQ" secondAttribute="trailing" constant="21" id="YLV-d0-1us"/>
|
||||
<constraint firstItem="5ce-ZL-glQ" firstAttribute="leading" secondItem="Vxr-5V-V6R" secondAttribute="leading" constant="20" symbolic="YES" id="dmE-Zi-5FR"/>
|
||||
<constraint firstItem="5ce-ZL-glQ" firstAttribute="top" secondItem="Vxr-5V-V6R" secondAttribute="top" constant="8" id="z4G-hO-VUE"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="TfM-Jc-Fr0">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="uFU-j6-qP1">
|
||||
<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="uFU-j6-qP1" id="fr4-mL-3Yf">
|
||||
<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" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Yl1-R6-xZi">
|
||||
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="words"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Yl1-R6-xZi" firstAttribute="leading" secondItem="fr4-mL-3Yf" secondAttribute="leading" constant="20" id="HJ4-VN-e9Y"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Yl1-R6-xZi" secondAttribute="trailing" constant="20" id="vbZ-dD-yZM"/>
|
||||
<constraint firstItem="Yl1-R6-xZi" firstAttribute="centerY" secondItem="fr4-mL-3Yf" secondAttribute="centerY" id="zsZ-z6-IFh"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="Sgf-NV-3Di">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="pTk-WJ-j5h" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="97.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="pTk-WJ-j5h" id="ahe-yz-PGg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mQv-3O-Y2d" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="EEL-8n-nHO"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Add Account">
|
||||
<color key="titleColor" name="secondaryAccentColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="add:" destination="vi3-jb-8XS" eventType="touchUpInside" id="lCb-LW-xZ0"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="mQv-3O-Y2d" firstAttribute="centerY" secondItem="ahe-yz-PGg" secondAttribute="centerY" id="6bl-bA-qYE"/>
|
||||
<constraint firstItem="mQv-3O-Y2d" firstAttribute="leading" secondItem="ahe-yz-PGg" secondAttribute="leading" id="7gZ-8n-bWs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="mQv-3O-Y2d" secondAttribute="trailing" id="FQu-yU-a4k"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="vi3-jb-8XS" id="U1Z-Kw-46j"/>
|
||||
<outlet property="delegate" destination="vi3-jb-8XS" id="4El-ci-jdg"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="On My Device" id="AOA-LS-PIB">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="b2H-re-cgN">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="vi3-jb-8XS" id="gRE-sR-r4Z"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="footerLabel" destination="5ce-ZL-glQ" id="V50-Yc-hD6"/>
|
||||
<outlet property="nameTextField" destination="Yl1-R6-xZi" id="jcl-vI-Rde"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="XJD-sO-MSq" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1879.7101449275365" y="144.64285714285714"/>
|
||||
</scene>
|
||||
<!--Feedbin-->
|
||||
<scene sceneID="IDj-HA-olN">
|
||||
<objects>
|
||||
<tableViewController id="ECy-jg-Kyc" customClass="FeedbinAccountViewController" 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="Y0x-RC-7ln">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="3KO-DU-JXG">
|
||||
<rect key="frame" x="0.0" y="202.5" width="414" height="150"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgL-0C-JZa">
|
||||
<rect key="frame" x="20" y="8" width="373" height="65.5"/>
|
||||
<string key="text">Sign in to your Feedbin account to sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.
|
||||
|
||||
Don’t have a Feedbin account?</string>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xhf-bK-vzm">
|
||||
<rect key="frame" x="172" y="72" width="70" height="26"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<state key="normal" title="Sign Up Here"/>
|
||||
<connections>
|
||||
<action selector="signUpWithProvider:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="fIY-hq-q3H"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="sgL-0C-JZa" firstAttribute="top" secondItem="3KO-DU-JXG" secondAttribute="top" constant="8" id="BgR-gH-qHf"/>
|
||||
<constraint firstItem="sgL-0C-JZa" firstAttribute="leading" secondItem="3KO-DU-JXG" secondAttribute="leading" constant="20" symbolic="YES" id="PLI-kz-MMq"/>
|
||||
<constraint firstItem="Xhf-bK-vzm" firstAttribute="top" secondItem="sgL-0C-JZa" secondAttribute="bottom" constant="-1.5" id="R9l-5y-aMr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sgL-0C-JZa" secondAttribute="trailing" constant="21" id="ddS-HE-f1J"/>
|
||||
<constraint firstItem="Xhf-bK-vzm" firstAttribute="centerX" secondItem="3KO-DU-JXG" secondAttribute="centerX" id="xs6-P4-4Vj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="xBN-Pb-KAy">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="lsa-Fl-Pc7">
|
||||
<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="lsa-Fl-Pc7" id="Lpd-D1-1PQ">
|
||||
<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" placeholder="Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vJa-NN-yjR">
|
||||
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="vJa-NN-yjR" secondAttribute="trailing" constant="20" id="7xY-Mz-Szf"/>
|
||||
<constraint firstItem="vJa-NN-yjR" firstAttribute="centerY" secondItem="Lpd-D1-1PQ" secondAttribute="centerY" id="E8M-nD-KIN"/>
|
||||
<constraint firstItem="vJa-NN-yjR" firstAttribute="leading" secondItem="Lpd-D1-1PQ" secondAttribute="leading" constant="20" id="Lgm-1L-4xL"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Hwv-Q0-zT0">
|
||||
<rect key="frame" x="20" y="61.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="Hwv-Q0-zT0" id="jIT-5L-d8d">
|
||||
<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" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="YC2-RH-QoV">
|
||||
<rect key="frame" x="20" y="13.5" width="290" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TfW-wf-V06">
|
||||
<rect key="frame" x="318" y="7.5" width="36" height="29"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Show"/>
|
||||
<connections>
|
||||
<action selector="showHidePassword:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="QcS-lr-SPG"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="TfW-wf-V06" firstAttribute="leading" secondItem="YC2-RH-QoV" secondAttribute="trailing" constant="8" symbolic="YES" id="MHu-Ut-Kox"/>
|
||||
<constraint firstItem="TfW-wf-V06" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="O3Y-Jd-n9t"/>
|
||||
<constraint firstItem="YC2-RH-QoV" firstAttribute="leading" secondItem="jIT-5L-d8d" secondAttribute="leading" constant="20" id="W79-WW-Buk"/>
|
||||
<constraint firstItem="YC2-RH-QoV" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="iDt-ym-Qjf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="TfW-wf-V06" secondAttribute="trailing" constant="20" symbolic="YES" id="rMZ-af-tPg"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="Kkf-rn-qdv">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="hWd-EN-p7e">
|
||||
<rect key="frame" x="20" y="141" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hWd-EN-p7e" id="S8S-1a-vVf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gv7-yG-aE3" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="pt0-rn-0JI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Action">
|
||||
<color key="titleColor" name="secondaryAccentColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="action:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="79h-R2-s0C"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="gv7-yG-aE3" firstAttribute="leading" secondItem="S8S-1a-vVf" secondAttribute="leading" id="cbE-1E-Mem"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gv7-yG-aE3" secondAttribute="trailing" id="tQC-jk-evr"/>
|
||||
<constraint firstItem="gv7-yG-aE3" firstAttribute="centerY" secondItem="S8S-1a-vVf" secondAttribute="centerY" id="xSU-R7-XQ8"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="ECy-jg-Kyc" id="hUr-Xx-9Ho"/>
|
||||
<outlet property="delegate" destination="ECy-jg-Kyc" id="DKA-Lp-mNb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Feedbin" id="tYg-9f-kyd">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="pfF-Of-5NT">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="ECy-jg-Kyc" id="ZKI-gV-ylg"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" id="Xwp-LO-qff">
|
||||
<view key="customView" contentMode="scaleToFill" id="cn4-b1-uZa">
|
||||
<rect key="frame" x="374" 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="medium" translatesAutoresizingMaskIntoConstraints="NO" id="YvV-hB-lzT">
|
||||
<rect key="frame" x="36" y="6" 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>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="actionButton" destination="gv7-yG-aE3" id="ENc-5A-hQc"/>
|
||||
<outlet property="activityIndicator" destination="YvV-hB-lzT" id="n1F-tV-5ZV"/>
|
||||
<outlet property="cancelBarButtonItem" destination="pfF-Of-5NT" id="Zr3-qD-1Yi"/>
|
||||
<outlet property="emailTextField" destination="vJa-NN-yjR" id="nCF-9W-YsF"/>
|
||||
<outlet property="footerLabel" destination="sgL-0C-JZa" id="b6I-Mk-2K3"/>
|
||||
<outlet property="passwordTextField" destination="YC2-RH-QoV" id="qaX-0i-7jq"/>
|
||||
<outlet property="showHideButton" destination="TfW-wf-V06" id="PbL-67-Nrg"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="L24-0i-kyr" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3177" y="145"/>
|
||||
</scene>
|
||||
<!--Modal Navigation Controller-->
|
||||
<scene sceneID="j4N-ax-exh">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="NewsBlurAccountNavigationViewController" id="eE3-pu-HdL" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Fsp-NG-hoR">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="Cge-ND-NpD" kind="relationship" relationship="rootViewController" id="1D5-CN-liN"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8t3-0U-5vL" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4562" y="-528"/>
|
||||
</scene>
|
||||
<!--NewsBlur-->
|
||||
<scene sceneID="tfA-kz-P6O">
|
||||
<objects>
|
||||
<tableViewController id="Cge-ND-NpD" customClass="NewsBlurAccountViewController" 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="fLL-7i-HdK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="mgO-Iq-dEg">
|
||||
<rect key="frame" x="0.0" y="202.5" width="414" height="150"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fal-e8-3BB">
|
||||
<rect key="frame" x="20" y="8" width="373" height="65.5"/>
|
||||
<string key="text">Sign in to your NewsBlur account to sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.
|
||||
|
||||
Don’t have a NewsBlur account?</string>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YhB-G0-eeJ">
|
||||
<rect key="frame" x="172" y="72" width="70" height="26"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<state key="normal" title="Sign Up Here"/>
|
||||
<connections>
|
||||
<action selector="signUpWithProvider:" destination="Cge-ND-NpD" eventType="touchUpInside" id="Vfz-DD-Kwm"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YhB-G0-eeJ" firstAttribute="centerX" secondItem="mgO-Iq-dEg" secondAttribute="centerX" id="7r5-l6-NNv"/>
|
||||
<constraint firstItem="fal-e8-3BB" firstAttribute="leading" secondItem="mgO-Iq-dEg" secondAttribute="leading" constant="20" symbolic="YES" id="O4q-GI-2AO"/>
|
||||
<constraint firstItem="YhB-G0-eeJ" firstAttribute="top" secondItem="fal-e8-3BB" secondAttribute="bottom" constant="-1.5" id="UHc-sh-Xq4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="fal-e8-3BB" secondAttribute="trailing" constant="21" id="V0d-ny-GRE"/>
|
||||
<constraint firstItem="fal-e8-3BB" firstAttribute="top" secondItem="mgO-Iq-dEg" secondAttribute="top" constant="8" id="h5B-kg-rZj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="I5T-12-2jC">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="gAY-Bo-c0L">
|
||||
<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="gAY-Bo-c0L" id="mqD-6S-DIl">
|
||||
<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" placeholder="Username or Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="S4v-fs-DIO">
|
||||
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="S4v-fs-DIO" secondAttribute="trailing" constant="20" id="Upe-dm-4DP"/>
|
||||
<constraint firstItem="S4v-fs-DIO" firstAttribute="leading" secondItem="mqD-6S-DIl" secondAttribute="leading" constant="20" id="pQc-Fh-6T3"/>
|
||||
<constraint firstItem="S4v-fs-DIO" firstAttribute="centerY" secondItem="mqD-6S-DIl" secondAttribute="centerY" id="s9a-ew-C5W"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="iCK-kn-Au6">
|
||||
<rect key="frame" x="20" y="61.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="iCK-kn-Au6" id="9Ej-wB-9Tr">
|
||||
<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" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="fct-XR-fEa">
|
||||
<rect key="frame" x="20" y="13.5" width="290" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GY9-nr-jFb">
|
||||
<rect key="frame" x="318" y="7.5" width="36" height="29"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Show"/>
|
||||
<connections>
|
||||
<action selector="showHidePassword:" destination="Cge-ND-NpD" eventType="touchUpInside" id="8JH-LX-URH"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="GY9-nr-jFb" firstAttribute="centerY" secondItem="9Ej-wB-9Tr" secondAttribute="centerY" id="3jf-KC-nd8"/>
|
||||
<constraint firstItem="GY9-nr-jFb" firstAttribute="leading" secondItem="fct-XR-fEa" secondAttribute="trailing" constant="8" symbolic="YES" id="Ibr-pt-eGr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="GY9-nr-jFb" secondAttribute="trailing" constant="20" symbolic="YES" id="mcZ-cl-knP"/>
|
||||
<constraint firstItem="fct-XR-fEa" firstAttribute="leading" secondItem="9Ej-wB-9Tr" secondAttribute="leading" constant="20" id="u5f-tJ-8ce"/>
|
||||
<constraint firstItem="fct-XR-fEa" firstAttribute="centerY" secondItem="9Ej-wB-9Tr" secondAttribute="centerY" id="z5e-jg-0nm"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="L37-iZ-GVj">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="fyQ-K8-byV">
|
||||
<rect key="frame" x="20" y="141" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fyQ-K8-byV" id="CtR-ZJ-FG5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E1I-C4-JdL" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="yoo-36-msf"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Action">
|
||||
<color key="titleColor" name="secondaryAccentColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="action:" destination="Cge-ND-NpD" eventType="touchUpInside" id="YQw-1k-e8G"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="E1I-C4-JdL" firstAttribute="centerY" secondItem="CtR-ZJ-FG5" secondAttribute="centerY" id="2vc-Ys-4Cj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="E1I-C4-JdL" secondAttribute="trailing" id="SLX-wc-QR7"/>
|
||||
<constraint firstItem="E1I-C4-JdL" firstAttribute="leading" secondItem="CtR-ZJ-FG5" secondAttribute="leading" id="Veu-Wo-GSZ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="Cge-ND-NpD" id="u8B-p4-Vlv"/>
|
||||
<outlet property="delegate" destination="Cge-ND-NpD" id="RIw-V2-EJC"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="NewsBlur" id="jCQ-pH-6AD">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="bl6-Y1-wQ8">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="Cge-ND-NpD" id="9zR-LJ-IWk"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" id="4yi-H0-B9J">
|
||||
<view key="customView" contentMode="scaleToFill" id="8DU-L0-P6c">
|
||||
<rect key="frame" x="374" 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="medium" translatesAutoresizingMaskIntoConstraints="NO" id="HfW-jV-MjK">
|
||||
<rect key="frame" x="36" y="6" 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>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="actionButton" destination="E1I-C4-JdL" id="q2T-4o-c8i"/>
|
||||
<outlet property="activityIndicator" destination="HfW-jV-MjK" id="AIV-uG-9uC"/>
|
||||
<outlet property="cancelBarButtonItem" destination="bl6-Y1-wQ8" id="ohR-gW-5J2"/>
|
||||
<outlet property="footerLabel" destination="fal-e8-3BB" id="7Fq-Oz-aEx"/>
|
||||
<outlet property="passwordTextField" destination="fct-XR-fEa" id="fGL-4k-gZ6"/>
|
||||
<outlet property="showHideButton" destination="GY9-nr-jFb" id="1p9-9F-GMY"/>
|
||||
<outlet property="usernameTextField" destination="S4v-fs-DIO" id="B7I-yz-M0T"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8Ku-6P-yPg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4561" y="145"/>
|
||||
</scene>
|
||||
<!--Reader-->
|
||||
<scene sceneID="3fU-9I-RDp">
|
||||
<objects>
|
||||
<tableViewController id="MzG-hS-TpF" customClass="ReaderAPIAccountViewController" 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="bQC-XA-xWP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="6sa-hD-iAT">
|
||||
<rect key="frame" x="0.0" y="246" width="414" height="150"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Jj-p8-lYw">
|
||||
<rect key="frame" x="20" y="8" width="373" height="39.5"/>
|
||||
<string key="text">Use your Reader account to sync your feeds across your devices.
|
||||
|
||||
Don’t have a Reader account?</string>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3Fq-U4-PS5">
|
||||
<rect key="frame" x="172" y="46" width="70" height="26"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<state key="normal" title="Sign Up Here"/>
|
||||
<connections>
|
||||
<action selector="signUpWithProvider:" destination="MzG-hS-TpF" eventType="touchUpInside" id="pMC-f8-E4G"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3Fq-U4-PS5" firstAttribute="top" secondItem="7Jj-p8-lYw" secondAttribute="bottom" constant="-1.5" id="6ma-pw-DSZ"/>
|
||||
<constraint firstItem="7Jj-p8-lYw" firstAttribute="leading" secondItem="6sa-hD-iAT" secondAttribute="leading" constant="20" symbolic="YES" id="CnY-UA-F3a"/>
|
||||
<constraint firstItem="3Fq-U4-PS5" firstAttribute="centerX" secondItem="6sa-hD-iAT" secondAttribute="centerX" id="Rgg-oG-KOZ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7Jj-p8-lYw" secondAttribute="trailing" constant="21" id="mgT-t6-6c8"/>
|
||||
<constraint firstItem="7Jj-p8-lYw" firstAttribute="top" secondItem="6sa-hD-iAT" secondAttribute="top" constant="8" id="syN-5x-dbM"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="Rju-xt-yUY">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="kW8-SV-Byq">
|
||||
<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="kW8-SV-Byq" id="4mV-Au-W6t">
|
||||
<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" placeholder="Username or Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="CZg-x8-936">
|
||||
<rect key="frame" x="14" y="12.5" width="347" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="CZg-x8-936" secondAttribute="trailing" constant="13" id="7BW-D6-ZAW"/>
|
||||
<constraint firstItem="CZg-x8-936" firstAttribute="centerY" secondItem="4mV-Au-W6t" secondAttribute="centerY" id="Fii-Qu-oXf"/>
|
||||
<constraint firstItem="CZg-x8-936" firstAttribute="leading" secondItem="4mV-Au-W6t" secondAttribute="leading" constant="14" id="MKL-Nm-1Po"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="pNe-n6-tVf">
|
||||
<rect key="frame" x="20" y="61.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="pNe-n6-tVf" id="yQJ-L0-qVZ">
|
||||
<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" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="KgN-kQ-Cyc">
|
||||
<rect key="frame" x="14" y="13.5" width="300" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cFF-qt-WLs">
|
||||
<rect key="frame" x="322" y="7.5" width="36" height="29"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Show"/>
|
||||
<connections>
|
||||
<action selector="showHidePassword:" destination="MzG-hS-TpF" eventType="touchUpInside" id="IsF-iJ-oxT"/>
|
||||
<action selector="showHidePassword:" destination="Cge-ND-NpD" eventType="touchUpInside" id="b9p-LX-Wk7"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="cFF-qt-WLs" firstAttribute="leading" secondItem="KgN-kQ-Cyc" secondAttribute="trailing" constant="8" symbolic="YES" id="Cwh-XX-m2G"/>
|
||||
<constraint firstItem="cFF-qt-WLs" firstAttribute="centerY" secondItem="yQJ-L0-qVZ" secondAttribute="centerY" id="GDc-9f-afL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="cFF-qt-WLs" secondAttribute="trailing" constant="16" id="K93-X3-UuK"/>
|
||||
<constraint firstItem="KgN-kQ-Cyc" firstAttribute="centerY" secondItem="yQJ-L0-qVZ" secondAttribute="centerY" id="UpQ-pU-DYv"/>
|
||||
<constraint firstItem="KgN-kQ-Cyc" firstAttribute="leading" secondItem="yQJ-L0-qVZ" secondAttribute="leading" constant="14" id="fam-16-kf6"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="mCx-af-pd3">
|
||||
<rect key="frame" x="20" y="105" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mCx-af-pd3" id="o1U-Qv-4gz">
|
||||
<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" placeholder="API URL" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="iPv-M2-U8Q">
|
||||
<rect key="frame" x="14" y="11" width="340" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="url"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iPv-M2-U8Q" firstAttribute="leading" secondItem="o1U-Qv-4gz" secondAttribute="leading" constant="14" id="4UP-GO-kmh"/>
|
||||
<constraint firstItem="iPv-M2-U8Q" firstAttribute="top" secondItem="o1U-Qv-4gz" secondAttribute="topMargin" id="Gap-aN-LP7"/>
|
||||
<constraint firstItem="iPv-M2-U8Q" firstAttribute="trailing" secondItem="o1U-Qv-4gz" secondAttribute="trailingMargin" id="npR-r8-mpF"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="UWZ-Vu-0Pp">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="43.5" id="d3E-Ds-Thm">
|
||||
<rect key="frame" x="20" y="184.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="d3E-Ds-Thm" id="Frb-uH-Sff">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7L9-X7-1Oc">
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<state key="normal" title="Action"/>
|
||||
<connections>
|
||||
<action selector="action:" destination="MzG-hS-TpF" eventType="touchUpInside" id="d10-4f-ZUn"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="7L9-X7-1Oc" firstAttribute="centerY" secondItem="Frb-uH-Sff" secondAttribute="centerY" id="NVm-XD-zND"/>
|
||||
<constraint firstItem="7L9-X7-1Oc" firstAttribute="centerX" secondItem="Frb-uH-Sff" secondAttribute="centerX" id="YB9-O8-Z15"/>
|
||||
<constraint firstItem="7L9-X7-1Oc" firstAttribute="height" secondItem="Frb-uH-Sff" secondAttribute="height" multiplier="0.689655" constant="13.999999999999996" id="iNV-NE-jhW"/>
|
||||
<constraint firstItem="7L9-X7-1Oc" firstAttribute="width" secondItem="Frb-uH-Sff" secondAttribute="width" multiplier="0.893048" constant="40" id="lfQ-KQ-9nR"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="MzG-hS-TpF" id="QvG-cd-Q3P"/>
|
||||
<outlet property="delegate" destination="MzG-hS-TpF" id="o4Z-RV-8uW"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Reader" id="z3N-XM-EXU">
|
||||
<barButtonItem key="leftBarButtonItem" title="Cancel" id="n8H-ai-4Df">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="MzG-hS-TpF" id="a49-Fh-i1S"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" id="Ih6-jI-jFg">
|
||||
<view key="customView" contentMode="scaleToFill" id="gSl-PT-7DH">
|
||||
<rect key="frame" x="374" 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="medium" translatesAutoresizingMaskIntoConstraints="NO" id="pdn-6v-d9a">
|
||||
<rect key="frame" x="36" y="6" 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>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="actionButton" destination="7L9-X7-1Oc" id="VnP-sl-Cmd"/>
|
||||
<outlet property="activityIndicator" destination="pdn-6v-d9a" id="vgt-C6-fy6"/>
|
||||
<outlet property="apiURLTextField" destination="iPv-M2-U8Q" id="8kn-Xk-a8w"/>
|
||||
<outlet property="cancelBarButtonItem" destination="n8H-ai-4Df" id="u86-HH-HYC"/>
|
||||
<outlet property="footerLabel" destination="7Jj-p8-lYw" id="Tqv-qR-WBR"/>
|
||||
<outlet property="passwordTextField" destination="KgN-kQ-Cyc" id="A0K-JL-CEW"/>
|
||||
<outlet property="showHideButton" destination="cFF-qt-WLs" id="AxI-Gl-NdM"/>
|
||||
<outlet property="signUpButton" destination="3Fq-U4-PS5" id="Wuj-5g-vDH"/>
|
||||
<outlet property="usernameTextField" destination="CZg-x8-936" id="nUT-WL-fKD"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Fj8-E0-Aeh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="5260.8695652173919" y="144.64285714285714"/>
|
||||
</scene>
|
||||
<!--Modal Navigation Controller-->
|
||||
<scene sceneID="gfi-2F-rht">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="CloudKitAccountNavigationViewController" id="LhW-Dq-qqj" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MVG-BZ-ALL">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="qj9-Vr-VIU" kind="relationship" relationship="rootViewController" id="n8n-iF-qeC"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="z9f-5I-8GC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2533" y="-528"/>
|
||||
</scene>
|
||||
<!--iCloud-->
|
||||
<scene sceneID="ULt-VE-viU">
|
||||
<objects>
|
||||
<tableViewController id="qj9-Vr-VIU" customClass="CloudKitAccountViewController" 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="j6U-sh-M9y">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="iYz-ri-yys">
|
||||
<rect key="frame" x="0.0" y="79.5" width="414" height="150"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your iCloud account syncs your feeds across your Mac and iOS devices" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aFS-Y0-2MH">
|
||||
<rect key="frame" x="20" y="8" width="373" height="36"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vXG-7q-4qg">
|
||||
<rect key="frame" x="75" y="50.5" width="264" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" title="iCloud Syncing Limitations & Solutions"/>
|
||||
<connections>
|
||||
<action selector="openLimitationsAndSolutions:" destination="qj9-Vr-VIU" eventType="touchUpInside" id="JZ5-hQ-PLl"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vXG-7q-4qg" firstAttribute="centerX" secondItem="iYz-ri-yys" secondAttribute="centerX" id="8Pg-BU-zIj"/>
|
||||
<constraint firstItem="aFS-Y0-2MH" firstAttribute="leading" secondItem="iYz-ri-yys" secondAttribute="leading" constant="20" symbolic="YES" id="E97-lo-arw"/>
|
||||
<constraint firstItem="vXG-7q-4qg" firstAttribute="top" secondItem="aFS-Y0-2MH" secondAttribute="bottom" id="TEh-B3-9Ci"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aFS-Y0-2MH" secondAttribute="trailing" constant="21" id="XUo-oQ-MbK"/>
|
||||
<constraint firstItem="aFS-Y0-2MH" firstAttribute="top" secondItem="iYz-ri-yys" secondAttribute="top" constant="8" id="xpj-LW-4l7"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="bGn-Io-KuQ">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="FSY-KL-m3i" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<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="FSY-KL-m3i" id="ds7-ib-VgJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T1S-zH-rIp" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="dOv-Gz-h7s"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Use iCloud">
|
||||
<color key="titleColor" name="secondaryAccentColor"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="add:" destination="qj9-Vr-VIU" eventType="touchUpInside" id="kUm-lW-g62"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="T1S-zH-rIp" firstAttribute="leading" secondItem="ds7-ib-VgJ" secondAttribute="leading" id="7F5-Ym-ew3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="T1S-zH-rIp" secondAttribute="trailing" id="ON3-nQ-kd8"/>
|
||||
<constraint firstItem="T1S-zH-rIp" firstAttribute="centerY" secondItem="ds7-ib-VgJ" secondAttribute="centerY" id="dAM-F2-peY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="qj9-Vr-VIU" id="j7u-Yd-rbe"/>
|
||||
<outlet property="delegate" destination="qj9-Vr-VIU" id="NhE-Pt-JGp"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="iCloud" id="idp-kp-cGU">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="hKZ-OI-mTV">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="qj9-Vr-VIU" id="n5q-9M-3ME"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="footerLabel" destination="aFS-Y0-2MH" id="gDw-R1-HSK"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="weY-OS-9NV" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2533" y="145"/>
|
||||
</scene>
|
||||
<!--Modal Navigation Controller-->
|
||||
<scene sceneID="JBz-7C-wEc">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="ReaderAPIAccountNavigationViewController" id="Son-xT-GLx" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="sdL-X8-E6K">
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="MzG-hS-TpF" kind="relationship" relationship="rootViewController" id="gsQ-9o-AMJ"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="vls-xO-YVi" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="5261" y="-528"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<namedColor name="secondaryAccentColor">
|
||||
<color red="0.031372549019607843" green="0.41568627450980394" blue="0.93333333333333335" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGroupedBackgroundColor">
|
||||
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -6,11 +6,14 @@
|
||||
Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
*/
|
||||
|
||||
/* Account Names */
|
||||
/* Account Section */
|
||||
"ACCOUNT_NAME" = "Name";
|
||||
"CLOUDKIT" = "iCloud";
|
||||
"LOCAL_ACCOUNT_NAME_PHONE" = "On My iPhone";
|
||||
"LOCAL_ACCOUNT_NAME_PAD" = "On My iPad";
|
||||
"ACCOUNT_EMAIL_ADDRESS_PROMPT" = "Email Address";
|
||||
"ACCOUNT_USERNAME_PROMPT" = "Username";
|
||||
"ACCOUNT_PASSWORD_PROMPT" = "Password";
|
||||
|
||||
|
||||
/* Explainers */
|
||||
@@ -20,5 +23,7 @@
|
||||
"OLDREADER_FOOTER_EXPLAINER" = "Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account? [Sign Up Here](https://theoldreader.com)";
|
||||
"FRESHRSS_FOOTER_EXPLAINER" = "Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance? [Sign Up Here](https://freshrss.org)";
|
||||
"CLOUDKIT_FOOTER_EXPLAINER" = "NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.";
|
||||
"FEEDBIN_FOOTER_EXPLAINER" = "Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a Feedbin account? [Sign Up Here](https://feedbin.com/signup)";
|
||||
"NEWSBLUR_FOOTER_EXPLAINER" = "Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a NewsBlur account? [Sign Up Here](https://newsblur.com)";
|
||||
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// CloudKitAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 3/28/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Account
|
||||
|
||||
enum CloudKitAccountViewControllerError: LocalizedError {
|
||||
case iCloudDriveMissing
|
||||
|
||||
var errorDescription: String? {
|
||||
return NSLocalizedString("Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Preferences.", comment: "Unable to add iCloud Account.")
|
||||
}
|
||||
}
|
||||
|
||||
class CloudKitAccountViewController: UITableViewController {
|
||||
|
||||
//weak var delegate: AddAccountDismissDelegate?
|
||||
@IBOutlet weak var footerLabel: UILabel!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupFooter()
|
||||
|
||||
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||
}
|
||||
|
||||
private func setupFooter() {
|
||||
footerLabel.text = NSLocalizedString("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud")
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
//delegate?.dismiss()
|
||||
}
|
||||
|
||||
@IBAction func add(_ sender: Any) {
|
||||
guard FileManager.default.ubiquityIdentityToken != nil else {
|
||||
presentError(CloudKitAccountViewControllerError.iCloudDriveMissing)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = AccountManager.shared.createAccount(type: .cloudKit)
|
||||
dismiss(animated: true, completion: nil)
|
||||
//delegate?.dismiss()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = AppAssets.image(for: .cloudKit)
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
|
||||
let vc = SFSafariViewController(url: CloudKitWebDocumentation.limitationsAndSolutionsURL)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
//
|
||||
// FeedbinAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/19/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import Secrets
|
||||
import RSWeb
|
||||
import SafariServices
|
||||
import RSCore
|
||||
|
||||
class FeedbinAccountViewController: UITableViewController, Logging {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet weak var emailTextField: UITextField!
|
||||
@IBOutlet weak var passwordTextField: UITextField!
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
@IBOutlet weak var footerLabel: UILabel!
|
||||
|
||||
weak var account: Account?
|
||||
//weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupFooter()
|
||||
|
||||
activityIndicator.isHidden = true
|
||||
emailTextField.delegate = self
|
||||
passwordTextField.delegate = self
|
||||
|
||||
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
|
||||
actionButton.isEnabled = true
|
||||
emailTextField.text = credentials.username
|
||||
passwordTextField.text = credentials.secret
|
||||
} else {
|
||||
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: emailTextField)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
|
||||
|
||||
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||
|
||||
}
|
||||
|
||||
private func setupFooter() {
|
||||
footerLabel.text = NSLocalizedString("Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a Feedbin account?", comment: "Feedbin")
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = AppAssets.image(for: .feedbin)
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@IBAction func showHidePassword(_ sender: Any) {
|
||||
if passwordTextField.isSecureTextEntry {
|
||||
passwordTextField.isSecureTextEntry = false
|
||||
showHideButton.setTitle("Hide", for: .normal)
|
||||
} else {
|
||||
passwordTextField.isSecureTextEntry = true
|
||||
showHideButton.setTitle("Show", for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
guard let email = emailTextField.text, let password = passwordTextField.text else {
|
||||
showError(NSLocalizedString("Username & password required.", comment: "Credentials Error"))
|
||||
return
|
||||
}
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedEmail = email.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmail) else {
|
||||
showError(NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
||||
resignFirstResponder()
|
||||
toggleActivityIndicatorAnimation(visible: true)
|
||||
setNavigationEnabled(to: false)
|
||||
|
||||
let credentials = Credentials(type: .basic, username: trimmedEmail, secret: password)
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
self.toggleActivityIndicatorAnimation(visible: false)
|
||||
self.setNavigationEnabled(to: true)
|
||||
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let credentials = credentials {
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .feedbin)
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .basic)
|
||||
} catch {
|
||||
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
|
||||
}
|
||||
try self.account?.storeCredentials(credentials)
|
||||
|
||||
self.account?.refreshAll() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
//self.delegate?.dismiss()
|
||||
} catch {
|
||||
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
|
||||
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
|
||||
}
|
||||
} else {
|
||||
self.showError(NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error"))
|
||||
}
|
||||
case .failure:
|
||||
self.showError(NSLocalizedString("Network error. Try again later.", comment: "Credentials Error"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func signUpWithProvider(_ sender: Any) {
|
||||
let url = URL(string: "https://feedbin.com/signup")!
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.modalPresentationStyle = .currentContext
|
||||
self.present(safari, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc func textDidChange(_ note: Notification) {
|
||||
actionButton.isEnabled = !(emailTextField.text?.isEmpty ?? false) && !(passwordTextField.text?.isEmpty ?? false)
|
||||
}
|
||||
|
||||
private func showError(_ message: String) {
|
||||
presentError(title: NSLocalizedString("Error", comment: "Credentials Error"), message: message)
|
||||
}
|
||||
|
||||
private func setNavigationEnabled(to value:Bool){
|
||||
cancelBarButtonItem.isEnabled = value
|
||||
actionButton.isEnabled = value
|
||||
}
|
||||
|
||||
private func toggleActivityIndicatorAnimation(visible value: Bool){
|
||||
activityIndicator.isHidden = !value
|
||||
if value {
|
||||
activityIndicator.startAnimating()
|
||||
} else {
|
||||
activityIndicator.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FeedbinAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if textField == emailTextField {
|
||||
passwordTextField.becomeFirstResponder()
|
||||
} else {
|
||||
textField.resignFirstResponder()
|
||||
action(self)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
//
|
||||
// NewsBlurAccountViewController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Anh-Quang Do on 3/9/20.
|
||||
// Copyright (c) 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import Secrets
|
||||
import RSWeb
|
||||
import RSCore
|
||||
import SafariServices
|
||||
|
||||
class NewsBlurAccountViewController: UITableViewController, Logging {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet weak var usernameTextField: UITextField!
|
||||
@IBOutlet weak var passwordTextField: UITextField!
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
@IBOutlet weak var footerLabel: UILabel!
|
||||
|
||||
weak var account: Account?
|
||||
//weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupFooter()
|
||||
activityIndicator.isHidden = true
|
||||
usernameTextField.delegate = self
|
||||
passwordTextField.delegate = self
|
||||
|
||||
if let account = account, let credentials = try? account.retrieveCredentials(type: .newsBlurBasic) {
|
||||
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
|
||||
actionButton.isEnabled = true
|
||||
usernameTextField.text = credentials.username
|
||||
passwordTextField.text = credentials.secret
|
||||
} else {
|
||||
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: usernameTextField)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
|
||||
|
||||
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||
}
|
||||
|
||||
private func setupFooter() {
|
||||
footerLabel.text = NSLocalizedString("Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a NewsBlur account?", comment: "NewsBlur")
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = AppAssets.image(for: .newsBlur)
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@IBAction func showHidePassword(_ sender: Any) {
|
||||
if passwordTextField.isSecureTextEntry {
|
||||
passwordTextField.isSecureTextEntry = false
|
||||
showHideButton.setTitle("Hide", for: .normal)
|
||||
} else {
|
||||
passwordTextField.isSecureTextEntry = true
|
||||
showHideButton.setTitle("Show", for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
|
||||
guard let username = usernameTextField.text else {
|
||||
showError(NSLocalizedString("Username required.", comment: "Credentials Error"))
|
||||
return
|
||||
}
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedUsername) else {
|
||||
showError(NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
||||
let password = passwordTextField.text ?? ""
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedUsername, secret: password)
|
||||
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
|
||||
|
||||
self.stopAnimatingActivityIndicator()
|
||||
self.enableNavigation()
|
||||
|
||||
switch result {
|
||||
case .success(let sessionCredentials):
|
||||
if let sessionCredentials = sessionCredentials {
|
||||
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .newsBlur)
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionId)
|
||||
} catch {
|
||||
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
|
||||
}
|
||||
try self.account?.storeCredentials(basicCredentials)
|
||||
try self.account?.storeCredentials(sessionCredentials)
|
||||
|
||||
self.account?.refreshAll() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
//self.delegate?.dismiss()
|
||||
} catch {
|
||||
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
|
||||
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
|
||||
}
|
||||
} else {
|
||||
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))
|
||||
}
|
||||
case .failure(let error):
|
||||
self.showError(error.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func signUpWithProvider(_ sender: Any) {
|
||||
let url = URL(string: "https://newsblur.com")!
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.modalPresentationStyle = .currentContext
|
||||
self.present(safari, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func textDidChange(_ note: Notification) {
|
||||
actionButton.isEnabled = !(usernameTextField.text?.isEmpty ?? false)
|
||||
}
|
||||
|
||||
private func showError(_ message: String) {
|
||||
presentError(title: "Error", message: message)
|
||||
}
|
||||
|
||||
private func enableNavigation() {
|
||||
self.cancelBarButtonItem.isEnabled = true
|
||||
self.actionButton.isEnabled = true
|
||||
}
|
||||
|
||||
private func disableNavigation() {
|
||||
cancelBarButtonItem.isEnabled = false
|
||||
actionButton.isEnabled = false
|
||||
}
|
||||
|
||||
private func startAnimatingActivityIndicator() {
|
||||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
}
|
||||
|
||||
private func stopAnimatingActivityIndicator() {
|
||||
self.activityIndicator.isHidden = true
|
||||
self.activityIndicator.stopAnimating()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NewsBlurAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
//
|
||||
// ReaderAPIAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 25/10/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import Secrets
|
||||
import RSWeb
|
||||
import SafariServices
|
||||
import RSCore
|
||||
|
||||
class ReaderAPIAccountViewController: UITableViewController, Logging {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet weak var usernameTextField: UITextField!
|
||||
@IBOutlet weak var passwordTextField: UITextField!
|
||||
@IBOutlet weak var apiURLTextField: UITextField!
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
@IBOutlet weak var footerLabel: UILabel!
|
||||
@IBOutlet weak var signUpButton: UIButton!
|
||||
|
||||
weak var account: Account?
|
||||
var accountType: AccountType?
|
||||
//weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupFooter()
|
||||
|
||||
activityIndicator.isHidden = true
|
||||
usernameTextField.delegate = self
|
||||
passwordTextField.delegate = self
|
||||
|
||||
if let unwrappedAcount = account,
|
||||
let credentials = try? retrieveCredentialsForAccount(for: unwrappedAcount) {
|
||||
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
|
||||
actionButton.isEnabled = true
|
||||
usernameTextField.text = credentials.username
|
||||
passwordTextField.text = credentials.secret
|
||||
} else {
|
||||
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
|
||||
}
|
||||
|
||||
if let unwrappedAccountType = accountType {
|
||||
switch unwrappedAccountType {
|
||||
case .freshRSS:
|
||||
title = NSLocalizedString("FreshRSS", comment: "FreshRSS")
|
||||
apiURLTextField.placeholder = NSLocalizedString("API URL: fresh.rss.net/api/greader.php", comment: "FreshRSS API Helper")
|
||||
case .inoreader:
|
||||
title = NSLocalizedString("InoReader", comment: "InoReader")
|
||||
case .bazQux:
|
||||
title = NSLocalizedString("BazQux", comment: "BazQux")
|
||||
case .theOldReader:
|
||||
title = NSLocalizedString("The Old Reader", comment: "The Old Reader")
|
||||
default:
|
||||
title = ""
|
||||
}
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: usernameTextField)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
|
||||
|
||||
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
|
||||
|
||||
}
|
||||
|
||||
private func setupFooter() {
|
||||
switch accountType {
|
||||
case .bazQux:
|
||||
footerLabel.text = NSLocalizedString("Sign in to your BazQux account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a BazQux account?", comment: "BazQux")
|
||||
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "BazQux SignUp"), for: .normal)
|
||||
case .inoreader:
|
||||
footerLabel.text = NSLocalizedString("Sign in to your InoReader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an InoReader account?", comment: "InoReader")
|
||||
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "InoReader SignUp"), for: .normal)
|
||||
case .theOldReader:
|
||||
footerLabel.text = NSLocalizedString("Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account?", comment: "TOR")
|
||||
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "TOR SignUp"), for: .normal)
|
||||
case .freshRSS:
|
||||
footerLabel.text = NSLocalizedString("Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance?", comment: "FreshRSS")
|
||||
signUpButton.setTitle(NSLocalizedString("Find Out More", comment: "FreshRSS SignUp"), for: .normal)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = headerViewImage()
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0:
|
||||
switch accountType {
|
||||
case .freshRSS:
|
||||
return 3
|
||||
default:
|
||||
return 2
|
||||
}
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@IBAction func showHidePassword(_ sender: Any) {
|
||||
if passwordTextField.isSecureTextEntry {
|
||||
passwordTextField.isSecureTextEntry = false
|
||||
showHideButton.setTitle("Hide", for: .normal)
|
||||
} else {
|
||||
passwordTextField.isSecureTextEntry = true
|
||||
showHideButton.setTitle("Show", for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
guard validateDataEntry(), let type = accountType else {
|
||||
return
|
||||
}
|
||||
|
||||
let username = usernameTextField.text!
|
||||
let password = passwordTextField.text!
|
||||
let url = apiURL()!
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: type, username: trimmedUsername) else {
|
||||
showError(NSLocalizedString("There is already an account of that type with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
let credentials = Credentials(type: .readerBasic, username: trimmedUsername, secret: password)
|
||||
Account.validateCredentials(type: type, credentials: credentials, endpoint: url) { result in
|
||||
|
||||
self.stopAnimatingActivityIndicator()
|
||||
self.enableNavigation()
|
||||
|
||||
switch result {
|
||||
case .success(let validatedCredentials):
|
||||
if let validatedCredentials = validatedCredentials {
|
||||
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: type)
|
||||
}
|
||||
|
||||
do {
|
||||
self.account?.endpointURL = url
|
||||
|
||||
try? self.account?.removeCredentials(type: .readerBasic)
|
||||
try? self.account?.removeCredentials(type: .readerAPIKey)
|
||||
try self.account?.storeCredentials(credentials)
|
||||
try self.account?.storeCredentials(validatedCredentials)
|
||||
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
|
||||
self.account?.refreshAll() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self.showError(NSLocalizedString(error.localizedDescription, comment: "Accoount Refresh Error"))
|
||||
}
|
||||
}
|
||||
|
||||
//self.delegate?.dismiss()
|
||||
} catch {
|
||||
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
|
||||
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
|
||||
}
|
||||
} else {
|
||||
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))
|
||||
}
|
||||
case .failure(let error):
|
||||
self.showError(error.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private func retrieveCredentialsForAccount(for account: Account) throws -> Credentials? {
|
||||
switch accountType {
|
||||
case .bazQux, .inoreader, .theOldReader, .freshRSS:
|
||||
return try account.retrieveCredentials(type: .readerBasic)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func headerViewImage() -> UIImage? {
|
||||
if let accountType = accountType {
|
||||
switch accountType {
|
||||
case .bazQux:
|
||||
return AppAssets.accountBazQuxImage
|
||||
case .inoreader:
|
||||
return AppAssets.accountInoreaderImage
|
||||
case .theOldReader:
|
||||
return AppAssets.accountTheOldReaderImage
|
||||
case .freshRSS:
|
||||
return AppAssets.accountFreshRSSImage
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func validateDataEntry() -> Bool {
|
||||
switch accountType {
|
||||
case .freshRSS:
|
||||
if !usernameTextField.hasText || !passwordTextField.hasText || !apiURLTextField.hasText {
|
||||
showError(NSLocalizedString("Username, password, and API URL are required.", comment: "Credentials Error"))
|
||||
return false
|
||||
}
|
||||
guard let _ = URL(string: apiURLTextField.text!) else {
|
||||
showError(NSLocalizedString("Invalid API URL.", comment: "Invalid API URL"))
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !usernameTextField.hasText || !passwordTextField.hasText {
|
||||
showError(NSLocalizedString("Username and password are required.", comment: "Credentials Error"))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@IBAction func signUpWithProvider(_ sender: Any) {
|
||||
var url: URL!
|
||||
switch accountType {
|
||||
case .bazQux:
|
||||
url = URL(string: "https://bazqux.com")!
|
||||
case .inoreader:
|
||||
url = URL(string: "https://www.inoreader.com")!
|
||||
case .theOldReader:
|
||||
url = URL(string: "https://theoldreader.com")!
|
||||
case .freshRSS:
|
||||
url = URL(string: "https://freshrss.org")!
|
||||
default:
|
||||
return
|
||||
}
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.modalPresentationStyle = .currentContext
|
||||
self.present(safari, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func apiURL() -> URL? {
|
||||
switch accountType {
|
||||
case .freshRSS:
|
||||
return URL(string: apiURLTextField.text!)!
|
||||
case .inoreader:
|
||||
return URL(string: ReaderAPIVariant.inoreader.host)!
|
||||
case .bazQux:
|
||||
return URL(string: ReaderAPIVariant.bazQux.host)!
|
||||
case .theOldReader:
|
||||
return URL(string: ReaderAPIVariant.theOldReader.host)!
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@objc func textDidChange(_ note: Notification) {
|
||||
actionButton.isEnabled = !(usernameTextField.text?.isEmpty ?? false)
|
||||
}
|
||||
|
||||
private func showError(_ message: String) {
|
||||
presentError(title: "Error", message: message)
|
||||
}
|
||||
|
||||
private func enableNavigation() {
|
||||
self.cancelBarButtonItem.isEnabled = true
|
||||
self.actionButton.isEnabled = true
|
||||
}
|
||||
|
||||
private func disableNavigation() {
|
||||
cancelBarButtonItem.isEnabled = false
|
||||
actionButton.isEnabled = false
|
||||
}
|
||||
|
||||
private func startAnimatingActivityIndicator() {
|
||||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
}
|
||||
|
||||
private func stopAnimatingActivityIndicator() {
|
||||
self.activityIndicator.isHidden = true
|
||||
self.activityIndicator.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
extension ReaderAPIAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@ struct CloudKitAddAccountView: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section(header: cloudKitHeader) {}
|
||||
AccountSectionHeader(accountType: .cloudKit)
|
||||
Section { createCloudKitAccount }
|
||||
Section(footer: cloudKitExplainer) {}
|
||||
}
|
||||
@@ -33,23 +33,11 @@ struct CloudKitAddAccountView: View {
|
||||
} message: {
|
||||
Text(addAccountError.0?.localizedDescription ?? "Unknown Error")
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
.dismissOnExternalContextLaunch()
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
var cloudKitHeader: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(uiImage: AppAssets.accountCloudKitImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
var createCloudKitAccount: some View {
|
||||
Button {
|
||||
guard FileManager.default.ubiquityIdentityToken != nil else {
|
||||
|
||||
167
iOS/Account/Views/FeedbinAddAccountView.swift
Normal file
167
iOS/Account/Views/FeedbinAddAccountView.swift
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// FeedbinAddAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 18/12/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
import Secrets
|
||||
import RSWeb
|
||||
import SafariServices
|
||||
import RSCore
|
||||
|
||||
|
||||
struct FeedbinAddAccountView: View {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State var account: Account? = nil
|
||||
@State private var accountEmail: String = ""
|
||||
@State private var accountPassword: String = ""
|
||||
@State private var showProgressIndicator: Bool = false
|
||||
@State private var accountError: (Error?, Bool) = (nil, false)
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
AccountSectionHeader(accountType: .feedbin)
|
||||
accountDetails
|
||||
accountButton
|
||||
Section(footer: feedbinAccountExplainer) {}
|
||||
}
|
||||
.task {
|
||||
retrieveCredentials()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
|
||||
.disabled(showProgressIndicator)
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if showProgressIndicator { ProgressView() }
|
||||
}
|
||||
}
|
||||
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
|
||||
Button(role: .cancel) {
|
||||
//
|
||||
} label: {
|
||||
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
|
||||
}
|
||||
} message: {
|
||||
Text(accountError.0?.localizedDescription ?? "Error")
|
||||
}
|
||||
.navigationTitle(Text(account?.type.localizedAccountName() ?? ""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.dismissOnExternalContextLaunch()
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
var accountDetails: some View {
|
||||
Section {
|
||||
TextField("Email", text: $accountEmail, prompt: Text("ACCOUNT_EMAIL_ADDRESS_PROMPT", tableName: "Account"))
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account"))
|
||||
}
|
||||
}
|
||||
|
||||
var accountButton: some View {
|
||||
Section {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
try await executeAccountCredentials()
|
||||
dismiss()
|
||||
} catch {
|
||||
accountError = (error, true)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack{
|
||||
Spacer()
|
||||
if account == nil {
|
||||
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
|
||||
} else {
|
||||
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var feedbinAccountExplainer: some View {
|
||||
Text("FEEDBIN_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
private func retrieveCredentials() {
|
||||
if let account = account {
|
||||
do {
|
||||
if let creds = try account.retrieveCredentials(type: .basic) {
|
||||
accountEmail = creds.username
|
||||
accountPassword = creds.secret
|
||||
}
|
||||
} catch {
|
||||
accountError = (error, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func executeAccountCredentials() async throws {
|
||||
let trimmedEmailAddress = accountEmail.trimmingWhitespace
|
||||
|
||||
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmailAddress)) else {
|
||||
throw LocalizedNetNewsWireError.duplicateAccount
|
||||
}
|
||||
showProgressIndicator = true
|
||||
|
||||
let credentials = Credentials(type: .basic, username: trimmedEmailAddress, secret: accountPassword)
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let validatedCredentials = credentials {
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .feedbin)
|
||||
}
|
||||
|
||||
do {
|
||||
try? self.account?.removeCredentials(type: .basic)
|
||||
try self.account?.storeCredentials(validatedCredentials)
|
||||
self.account?.refreshAll(completion: { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
showProgressIndicator = false
|
||||
continuation.resume()
|
||||
case .failure(let failure):
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: failure)
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
|
||||
}
|
||||
} else {
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: LocalizedNetNewsWireError.invalidUsernameOrPassword)
|
||||
}
|
||||
case .failure(let failure):
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct FeedbinAddAccountView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
FeedbinAddAccountView()
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ struct LocalAddAccountView: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section(header: accountHeaderView) {}
|
||||
AccountSectionHeader(accountType: .onMyMac)
|
||||
Section { accountNameSection }
|
||||
Section { addAccountButton }
|
||||
Section(footer: accountFooterView) {}
|
||||
@@ -29,9 +29,8 @@ struct LocalAddAccountView: View {
|
||||
}
|
||||
.navigationTitle(deviceAccountName())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
.dismissOnExternalContextLaunch()
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +38,8 @@ struct LocalAddAccountView: View {
|
||||
TextField("Name",
|
||||
text: $accountName,
|
||||
prompt: Text("ACCOUNT_NAME", tableName: "Account"))
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
}
|
||||
|
||||
var addAccountButton: some View {
|
||||
@@ -54,17 +55,6 @@ struct LocalAddAccountView: View {
|
||||
}
|
||||
}
|
||||
|
||||
var accountHeaderView: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(uiImage: accountImage())
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
var accountFooterView: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
173
iOS/Account/Views/NewsBlurAddAccountView.swift
Normal file
173
iOS/Account/Views/NewsBlurAddAccountView.swift
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// NewsBlurAddAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 18/12/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
import Secrets
|
||||
import RSWeb
|
||||
import RSCore
|
||||
|
||||
struct NewsBlurAddAccountView: View {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State var account: Account? = nil
|
||||
@State private var accountUserName: String = ""
|
||||
@State private var accountPassword: String = ""
|
||||
@State private var showProgressIndicator: Bool = false
|
||||
@State private var accountError: (Error?, Bool) = (nil, false)
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
AccountSectionHeader(accountType: .newsBlur)
|
||||
accountDetails
|
||||
accountButton
|
||||
Section(footer: newsBlurAccountExplainer) {}
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
|
||||
.disabled(showProgressIndicator)
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if showProgressIndicator { ProgressView() }
|
||||
}
|
||||
}
|
||||
.navigationTitle(Text(AccountType.newsBlur.localizedAccountName()))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task {
|
||||
retreiveCredentials()
|
||||
}
|
||||
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
|
||||
Button(role: .cancel) {
|
||||
//
|
||||
} label: {
|
||||
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
|
||||
}
|
||||
} message: {
|
||||
Text(accountError.0?.localizedDescription ?? "")
|
||||
}
|
||||
.dismissOnExternalContextLaunch()
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
func retreiveCredentials() {
|
||||
if let account = account {
|
||||
do {
|
||||
let credentials = try account.retrieveCredentials(type: .newsBlurBasic)
|
||||
if let credentials = credentials {
|
||||
self.accountUserName = credentials.username
|
||||
self.accountPassword = credentials.secret
|
||||
} else {
|
||||
print("No cred")
|
||||
}
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
} else {
|
||||
print("No account")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var accountDetails: some View {
|
||||
Section {
|
||||
TextField("Email", text: $accountUserName, prompt: Text("ACCOUNT_USERNAME_PROMPT", tableName: "Account"))
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account"))
|
||||
}
|
||||
}
|
||||
|
||||
var accountButton: some View {
|
||||
Section {
|
||||
Button {
|
||||
Task {
|
||||
do {
|
||||
try await executeAccountCredentials()
|
||||
dismiss()
|
||||
} catch {
|
||||
accountError = (error, true)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack{
|
||||
Spacer()
|
||||
if account == nil {
|
||||
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
|
||||
} else {
|
||||
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var newsBlurAccountExplainer: some View {
|
||||
Text("NEWSBLUR_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
private func executeAccountCredentials() async throws {
|
||||
let trimmedEmailAddress = accountUserName.trimmingWhitespace
|
||||
|
||||
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedEmailAddress)) else {
|
||||
throw LocalizedNetNewsWireError.duplicateAccount
|
||||
}
|
||||
showProgressIndicator = true
|
||||
|
||||
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedEmailAddress, secret: accountPassword)
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let sessionsCredentials = credentials {
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .newsBlur)
|
||||
}
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionId)
|
||||
try self.account?.storeCredentials(basicCredentials)
|
||||
try self.account?.storeCredentials(sessionsCredentials)
|
||||
|
||||
self.account?.refreshAll(completion: { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
showProgressIndicator = false
|
||||
continuation.resume()
|
||||
case .failure(let failure):
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: failure)
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
|
||||
}
|
||||
} else {
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: LocalizedNetNewsWireError.invalidUsernameOrPassword)
|
||||
}
|
||||
case .failure(let failure):
|
||||
showProgressIndicator = false
|
||||
continuation.resume(throwing: failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NewsBlurAddAccountView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NewsBlurAddAccountView()
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,16 @@ struct ReaderAPIAddAccountView: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section(header: readerAccountImage) {}
|
||||
if accountType != nil {
|
||||
AccountSectionHeader(accountType: accountType!)
|
||||
}
|
||||
accountDetailsSection
|
||||
Section(footer: readerAccountExplainer) {}
|
||||
}
|
||||
.navigationTitle(Text(accountType?.localizedAccountName() ?? ""))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task {
|
||||
try? retrieveAccountCredentials()
|
||||
retrieveAccountCredentials()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
@@ -55,10 +57,8 @@ struct ReaderAPIAddAccountView: View {
|
||||
} message: {
|
||||
Text(accountSetupError.0?.localizedDescription ?? "")
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
.edgesIgnoringSafeArea(.bottom) // Fix to make sure view is not offset from the top when presented
|
||||
.dismissOnExternalContextLaunch()
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,47 +78,19 @@ struct ReaderAPIAddAccountView: View {
|
||||
}
|
||||
}
|
||||
|
||||
var readerAccountImage: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
if accountType == nil { Text("") }
|
||||
switch accountType! {
|
||||
case .bazQux:
|
||||
Image(uiImage: AppAssets.accountBazQuxImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
case .inoreader:
|
||||
Image(uiImage: AppAssets.accountInoreaderImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
case .theOldReader:
|
||||
Image(uiImage: AppAssets.accountTheOldReaderImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
case .freshRSS:
|
||||
Image(uiImage: AppAssets.accountFreshRSSImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
default:
|
||||
Text("")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var accountDetailsSection: some View {
|
||||
Group {
|
||||
Section {
|
||||
TextField("Username", text: $accountUserName)
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
SecureField("Password", text: $accountSecret)
|
||||
if accountType == .freshRSS && accountCredentials == nil {
|
||||
TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text("fresh.rss.net/api/greader.php"))
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +99,7 @@ struct ReaderAPIAddAccountView: View {
|
||||
Task {
|
||||
do {
|
||||
try await executeAccountCredentials()
|
||||
dismiss()
|
||||
} catch {
|
||||
accountSetupError = (error, true)
|
||||
}
|
||||
@@ -150,7 +123,7 @@ struct ReaderAPIAddAccountView: View {
|
||||
|
||||
// MARK: - API
|
||||
|
||||
private func retrieveAccountCredentials() throws {
|
||||
private func retrieveAccountCredentials() {
|
||||
if let account = account {
|
||||
do {
|
||||
if let creds = try account.retrieveCredentials(type: .readerBasic) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/* Account Inspector */
|
||||
"ACCOUNT_NAME_FIELD" = "Account Name";
|
||||
"ACTIVE" = "Active";
|
||||
"CLOUDKIT_LIMITATIONS_TITLE" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)";
|
||||
"CLOUDKIT_LIMITATIONS_LINK" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)";
|
||||
"REMOVE_ACCOUNT_TITLE" = "Remove Account";
|
||||
"REMOVE_FEEDLY_MESSAGE" = "Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again.";
|
||||
"REMOVE_ACCOUNT_MESSAGE" = "Are you sure you want to remove this account? This cannot be undone.";
|
||||
|
||||
@@ -20,7 +20,7 @@ struct AccountInspectorView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header: accountHeaderView){}
|
||||
AccountSectionHeader(accountType: account.type)
|
||||
accountNameAndActiveSection
|
||||
|
||||
if account.type != .onMyMac &&
|
||||
@@ -46,13 +46,14 @@ struct AccountInspectorView: View {
|
||||
case .theOldReader, .bazQux, .inoreader, .freshRSS:
|
||||
ReaderAPIAddAccountView(accountType: account.type, account: account)
|
||||
case .feedbin:
|
||||
Text("FEEDBIN")
|
||||
FeedbinAddAccountView(account: account)
|
||||
case .newsBlur:
|
||||
Text("NEWSBLUR")
|
||||
NewsBlurAddAccountView(account: account)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.dismissOnExternalContextLaunch()
|
||||
}
|
||||
|
||||
var accountHeaderView: some View {
|
||||
@@ -135,7 +136,7 @@ struct AccountInspectorView: View {
|
||||
var cloudKitLimitations: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("CLOUDKIT_LIMITATIONS_TITLE", tableName: "Inspector")
|
||||
Text("CLOUDKIT_LIMITATIONS_LINK", tableName: "Inspector")
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ struct ExtensionInspectorView: View {
|
||||
}
|
||||
.navigationTitle(Text(extensionPoint?.title ?? ""))
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
.dismissOnExternalContextLaunch()
|
||||
}
|
||||
|
||||
|
||||
var extensionHeader: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
@@ -63,6 +63,7 @@ struct WebFeedInspectorView: View {
|
||||
.sheet(isPresented: $showHomePage, onDismiss: nil) {
|
||||
SafariView(url: URL(string: webFeed.homePageURL!)!)
|
||||
}
|
||||
.dismissOnExternalContextLaunch()
|
||||
}
|
||||
|
||||
var webFeedHeaderView: some View {
|
||||
|
||||
@@ -66,7 +66,7 @@ struct AccountsManagementView: View {
|
||||
Button(role: .destructive) {
|
||||
AccountManager.shared.deleteAccount(accountToRemove!)
|
||||
} label: {
|
||||
Text("REMOVE_BUTTON_TITLE", tableName: "Buttons")
|
||||
Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
|
||||
}
|
||||
|
||||
Button(role: .cancel) {
|
||||
|
||||
@@ -90,15 +90,15 @@ struct AddAccountListView: View {
|
||||
LocalAddAccountView()
|
||||
case .cloudKit:
|
||||
CloudKitAddAccountView()
|
||||
case .newsBlur:
|
||||
NewsBlurAddAccountView()
|
||||
case .freshRSS, .inoreader, .bazQux, .theOldReader:
|
||||
ReaderAPIAddAccountView(accountType: viewModel.showAddAccountSheet.accountType, account: nil)
|
||||
default:
|
||||
Text(viewModel.showAddAccountSheet.accountType.localizedAccountName())
|
||||
}
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
.dismissOnAccountAdd()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,9 +108,7 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .LaunchedFromExternalAction), perform: { _ in
|
||||
dismiss()
|
||||
})
|
||||
.dismissOnExternalContextLaunch()
|
||||
.fileImporter(isPresented: $viewModel.showImportView, allowedContentTypes: OPMLDocument.readableContentTypes) { result in
|
||||
switch result {
|
||||
case .success(let url):
|
||||
|
||||
61
iOS/SwiftUI Extensions/AccountSectionHeader.swift
Normal file
61
iOS/SwiftUI Extensions/AccountSectionHeader.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// AccountSectionHeader.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 18/12/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct AccountSectionHeader: View {
|
||||
|
||||
var accountType: AccountType
|
||||
|
||||
var body: some View {
|
||||
Section(header: headerImage) {}
|
||||
}
|
||||
|
||||
var headerImage: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(uiImage: imageToUse())
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 48, height: 48)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
private func imageToUse() -> UIImage {
|
||||
switch accountType {
|
||||
case .onMyMac:
|
||||
if UIDevice.current.userInterfaceIdiom == .pad { return AppAssets.accountLocalPadImage }
|
||||
return AppAssets.accountLocalPhoneImage
|
||||
case .cloudKit:
|
||||
return AppAssets.accountCloudKitImage
|
||||
case .feedly:
|
||||
return AppAssets.accountFeedlyImage
|
||||
case .feedbin:
|
||||
return AppAssets.accountFeedbinImage
|
||||
case .newsBlur:
|
||||
return AppAssets.accountNewsBlurImage
|
||||
case .freshRSS:
|
||||
return AppAssets.accountFreshRSSImage
|
||||
case .inoreader:
|
||||
return AppAssets.accountInoreaderImage
|
||||
case .bazQux:
|
||||
return AppAssets.accountBazQuxImage
|
||||
case .theOldReader:
|
||||
return AppAssets.accountTheOldReaderImage
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct AccountHeader_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AccountSectionHeader(accountType: .onMyMac)
|
||||
}
|
||||
}
|
||||
28
iOS/SwiftUI Extensions/View+DismissOnAccountAdd.swift
Normal file
28
iOS/SwiftUI Extensions/View+DismissOnAccountAdd.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// View+DismissOnAccountAdd.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 18/12/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DismissOnAccountAdd: ViewModifier {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
func dismissOnAccountAdd() -> some View {
|
||||
modifier(DismissOnAccountAdd())
|
||||
}
|
||||
}
|
||||
29
iOS/SwiftUI Extensions/View+DismissOnExternalContext.swift
Normal file
29
iOS/SwiftUI Extensions/View+DismissOnExternalContext.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// View+DismissOnExternalContext.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 18/12/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct DismissOnExternalContext: ViewModifier {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.onReceive(NotificationCenter.default.publisher(for: .LaunchedFromExternalAction)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
func dismissOnExternalContextLaunch() -> some View {
|
||||
modifier(DismissOnExternalContext())
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,7 @@ import Account
|
||||
extension UIViewController {
|
||||
|
||||
func presentError(_ error: Error, dismiss: (() -> Void)? = nil) {
|
||||
if let accountError = error as? AccountError, accountError.isCredentialsError {
|
||||
presentAccountError(accountError, dismiss: dismiss)
|
||||
} else if let decodingError = error as? DecodingError {
|
||||
if let decodingError = error as? DecodingError {
|
||||
let errorTitle = NSLocalizedString("Error", comment: "Error")
|
||||
var informativeText: String = ""
|
||||
switch decodingError {
|
||||
@@ -53,38 +51,3 @@ extension UIViewController {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension UIViewController {
|
||||
|
||||
func presentAccountError(_ error: AccountError, dismiss: (() -> Void)? = nil) {
|
||||
let title = NSLocalizedString("Account Error", comment: "Account Error")
|
||||
let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert)
|
||||
|
||||
if error.account?.type == .feedbin {
|
||||
|
||||
let credentialsTitle = NSLocalizedString("Update Credentials", comment: "Update Credentials")
|
||||
let credentialsAction = UIAlertAction(title: credentialsTitle, style: .default) { [weak self] _ in
|
||||
dismiss?()
|
||||
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController
|
||||
navController.modalPresentationStyle = .formSheet
|
||||
let addViewController = navController.topViewController as! FeedbinAccountViewController
|
||||
addViewController.account = error.account
|
||||
self?.present(navController, animated: true)
|
||||
}
|
||||
|
||||
alertController.addAction(credentialsAction)
|
||||
alertController.preferredAction = credentialsAction
|
||||
|
||||
}
|
||||
|
||||
let dismissTitle = NSLocalizedString("OK", comment: "OK")
|
||||
let dismissAction = UIAlertAction(title: dismissTitle, style: .default) { _ in
|
||||
dismiss?()
|
||||
}
|
||||
alertController.addAction(dismissAction)
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user